summaryrefslogtreecommitdiff
path: root/src/syntax/func/values.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-26 15:51:13 +0100
commit20fb4e7c379b79b84d9884d5f2c89d781c5793e2 (patch)
treea1eef90680afa2b43cb1ce0a687c837fd78810e7 /src/syntax/func/values.rs
parent0a087cd28bbee5fcdffbb9d49b0ba9f413ad7f92 (diff)
Document everything 📜
Diffstat (limited to 'src/syntax/func/values.rs')
-rw-r--r--src/syntax/func/values.rs106
1 files changed, 87 insertions, 19 deletions
diff --git a/src/syntax/func/values.rs b/src/syntax/func/values.rs
index a767aef6..515d6a43 100644
--- a/src/syntax/func/values.rs
+++ b/src/syntax/func/values.rs
@@ -1,3 +1,5 @@
+//! Value types for extracting function arguments.
+
use std::fmt::{self, Display, Formatter};
use std::marker::PhantomData;
use toddle::query::{FontStyle, FontWeight};
@@ -10,9 +12,65 @@ use super::*;
use self::AlignmentValue::*;
+/// Value types are used to extract the values of positional and keyword
+/// arguments from [`Tuples`](crate::syntax::expr::Tuple) and
+/// [`Objects`](crate::syntax::expr::Object). They represent the value part of
+/// an argument.
+/// ```typst
+/// [func: value, key=value]
+/// ^^^^^ ^^^^^
+/// ```
+///
+/// Similarly to the [`Key`] trait, this trait has an associated output type
+/// which the values are parsed into. Most of the time this is just `Self`, as
+/// in the implementation for `bool`:
+/// ```
+/// # use typstc::err;
+/// # use typstc::error::Error;
+/// # use typstc::syntax::expr::Expr;
+/// # use typstc::syntax::func::Value;
+/// # use typstc::syntax::span::Spanned;
+/// # struct Bool; /*
+/// impl Value for bool {
+/// # */ impl Value for Bool {
+/// # type Output = bool; /*
+/// type Output = Self;
+/// # */
+///
+/// fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> {
+/// match expr.v {
+/// Expr::Bool(b) => Ok(b),
+/// other => Err(err!("expected bool, found {}", other.name())),
+/// }
+/// }
+/// }
+/// ```
+///
+/// However, sometimes the `Output` type is not just `Self`. For example, there
+/// is a value called `Defaultable<V>` which acts as follows:
+/// ```
+/// # use typstc::syntax::func::{FuncArgs, Defaultable};
+/// # use typstc::size::Size;
+/// # let mut args = FuncArgs::new();
+/// # let mut errors = vec![];
+/// args.key.get::<Defaultable<Size>>(&mut errors, "size");
+/// ```
+/// This will yield.
+/// ```typst
+/// [func: size=2cm] => Some(Size::cm(2.0))
+/// [func: size=default] => None
+/// ```
+///
+/// The type `Defaultable` has no fields and is only used for extracting the
+/// option value. This prevents us from having a `Defaultable<V>` type which is
+/// essentially simply a bad [`Option`] replacement without the good utility
+/// functions.
pub trait Value {
+ /// The type to parse into.
type Output;
+ /// Parse an expression into this value or return an error if the expression
+ /// is valid for this value type.
fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error>;
}
@@ -25,6 +83,7 @@ impl<V: Value> Value for Spanned<V> {
}
}
+/// Implements [`Value`] for types that just need to match on expressions.
macro_rules! value {
($type:ty, $output:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => {
impl Value for $type {
@@ -57,6 +116,8 @@ value!(ScaleSize, Self, "number or size",
Expr::Number(scale) => ScaleSize::Scaled(scale as f32),
);
+/// A value type that matches [`Expr::Ident`] and [`Expr::Str`] and returns a
+/// String.
pub struct StringLike;
value!(StringLike, String, "identifier or string",
@@ -64,15 +125,18 @@ value!(StringLike, String, "identifier or string",
Expr::Str(s) => s,
);
-pub struct Defaultable<T>(PhantomData<T>);
+/// A value type that matches the string `"default"` or a value type `V` and
+/// returns `Option::Some(V::Output)` for a value and `Option::None` for
+/// `"default"`.
+pub struct Defaultable<V>(PhantomData<V>);
-impl<T: Value> Value for Defaultable<T> {
- type Output = Option<T::Output>;
+impl<V: Value> Value for Defaultable<V> {
+ type Output = Option<V::Output>;
fn parse(expr: Spanned<Expr>) -> Result<Self::Output, Error> {
match expr.v {
Expr::Ident(ident) if ident.as_str() == "default" => Ok(None),
- _ => T::parse(expr).map(Some)
+ _ => V::parse(expr).map(Some)
}
}
}
@@ -135,8 +199,12 @@ impl Value for Direction {
}
}
+/// A value type that matches identifiers that are valid alignments like
+/// `origin` or `right`.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+#[allow(missing_docs)]
pub enum AlignmentValue {
+ /// A generic alignment.
Align(Alignment),
Left,
Top,
@@ -145,26 +213,26 @@ pub enum AlignmentValue {
}
impl AlignmentValue {
- /// The generic axis this alignment corresponds to in the given system of
- /// layouting axes. `None` if the alignment is generic.
- pub fn axis(self, axes: LayoutAxes) -> Option<GenericAxis> {
+ /// The specific axis this alignment corresponds to. `None` if the alignment
+ /// is generic.
+ pub fn axis(self) -> Option<SpecificAxis> {
match self {
- Left | Right => Some(Horizontal.to_generic(axes)),
- Top | Bottom => Some(Vertical.to_generic(axes)),
+ Left | Right => Some(Horizontal),
+ Top | Bottom => Some(Vertical),
Align(_) => None,
}
}
- /// The generic version of this alignment in the given system of layouting
- /// axes.
+ /// The generic version of this alignment on the given axis in the given
+ /// system of layouting axes.
///
/// Returns `None` if the alignment is invalid for the given axis.
pub fn to_generic(self, axes: LayoutAxes, axis: GenericAxis) -> Option<Alignment> {
let specific = axis.to_specific(axes);
- let start = match axes.get(axis).is_positive() {
- true => Origin,
- false => End,
- };
+ let positive = axes.get(axis).is_positive();
+
+ // The alignment matching the origin of the positive coordinate direction.
+ let start = if positive { Origin } else { End };
match (self, specific) {
(Align(alignment), _) => Some(alignment),
@@ -174,10 +242,10 @@ impl AlignmentValue {
}
}
- /// The specific version of this alignment in the given system of layouting
- /// axes.
- pub fn to_specific(self, axes: LayoutAxes, axis: SpecificAxis) -> AlignmentValue {
- let direction = axes.get_specific(axis);
+ /// The specific version of this alignment on the given axis in the given
+ /// system of layouting axes.
+ pub fn to_specific(self, axes: LayoutAxes, axis: GenericAxis) -> AlignmentValue {
+ let direction = axes.get(axis);
if let Align(alignment) = self {
match (direction, alignment) {
(LeftToRight, Origin) | (RightToLeft, End) => Left,