summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-04-08 14:48:02 +0200
committerLaurenz <laurmaedje@gmail.com>2022-04-08 14:48:02 +0200
commite1d7edb7c1845e6df6f5e23e3baf7bc88159eade (patch)
tree4256919d2be06abdaf5267ac804cfc9d4d3a8dc5
parent4bb6240b401605ef6d905273db07545e14f9a21f (diff)
Property resolving
-rw-r--r--macros/src/lib.rs28
-rw-r--r--src/eval/styles.rs72
-rw-r--r--src/eval/value.rs37
-rw-r--r--src/geom/relative.rs9
4 files changed, 121 insertions, 25 deletions
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 09739b05..429c5b09 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -153,10 +153,12 @@ fn process_const(
// The type of the property's value is what the user of our macro wrote
// as type of the const ...
let value_ty = &item.ty;
- let output_ty = if property.fold {
- parse_quote!(<#value_ty as eval::Fold>::Output)
- } else if property.referenced {
+ let output_ty = if property.referenced {
parse_quote!(&'a #value_ty)
+ } else if property.fold {
+ parse_quote!(<#value_ty as eval::Fold>::Output)
+ } else if property.resolve {
+ parse_quote!(<#value_ty as eval::Resolve>::Output)
} else {
value_ty.clone()
};
@@ -191,10 +193,15 @@ fn process_const(
} else if property.fold {
get = quote! {
match values.next().cloned() {
- Some(inner) => eval::Fold::fold(inner, Self::get(values)),
+ Some(inner) => eval::Fold::fold(inner, Self::get(chain, values)),
None => #default,
}
};
+ } else if property.resolve {
+ get = quote! {
+ let value = values.next().cloned().unwrap_or(#default);
+ eval::Resolve::resolve(value, chain)
+ };
} else {
get = quote! {
values.next().copied().unwrap_or(#default)
@@ -227,13 +234,17 @@ fn process_const(
impl<'a, #params> eval::Key<'a> for #key {
type Value = #value_ty;
type Output = #output_ty;
+
const NAME: &'static str = #name;
fn node() -> TypeId {
TypeId::of::<#self_ty>()
}
- fn get(mut values: impl Iterator<Item = &'a Self::Value>) -> Self::Output {
+ fn get(
+ chain: StyleChain<'a>,
+ mut values: impl Iterator<Item = &'a Self::Value>,
+ ) -> Self::Output {
#get
}
}
@@ -257,6 +268,7 @@ struct Property {
shorthand: bool,
variadic: bool,
fold: bool,
+ resolve: bool,
}
/// Parse a style property attribute.
@@ -268,6 +280,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
shorthand: false,
variadic: false,
fold: false,
+ resolve: false,
};
if let Some(idx) = item
@@ -284,6 +297,7 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
"referenced" => property.referenced = true,
"variadic" => property.variadic = true,
"fold" => property.fold = true,
+ "resolve" => property.resolve = true,
_ => return Err(Error::new(ident.span(), "invalid attribute")),
},
TokenTree::Punct(_) => {}
@@ -300,10 +314,10 @@ fn parse_property(item: &mut syn::ImplItemConst) -> Result<Property> {
));
}
- if property.referenced && property.fold {
+ if property.referenced as u8 + property.fold as u8 + property.resolve as u8 > 1 {
return Err(Error::new(
span,
- "referenced and fold are mutually exclusive",
+ "referenced, fold and resolve are mutually exclusive",
));
}
diff --git a/src/eval/styles.rs b/src/eval/styles.rs
index b7f1889c..575518f5 100644
--- a/src/eval/styles.rs
+++ b/src/eval/styles.rs
@@ -4,8 +4,9 @@ use std::hash::Hash;
use std::marker::PhantomData;
use std::sync::Arc;
-use super::{Args, Content, Func, Layout, Node, Span, Value};
+use super::{Args, Content, Func, Layout, Node, Smart, Span, Value};
use crate::diag::{At, TypResult};
+use crate::geom::{Numeric, Relative, Sides, Spec};
use crate::library::layout::PageNode;
use crate::library::text::{FontFamily, ParNode, TextNode};
use crate::util::Prehashed;
@@ -280,7 +281,10 @@ pub trait Key<'a>: 'static {
/// Compute an output value from a sequence of values belong to this key,
/// folding if necessary.
- fn get(values: impl Iterator<Item = &'a Self::Value>) -> Self::Output;
+ fn get(
+ chain: StyleChain<'a>,
+ values: impl Iterator<Item = &'a Self::Value>,
+ ) -> Self::Output;
}
/// A property that is folded to determine its final value.
@@ -292,6 +296,64 @@ pub trait Fold {
fn fold(self, outer: Self::Output) -> Self::Output;
}
+/// A property that is resolved with other properties from the style chain.
+pub trait Resolve {
+ /// The type of the resolved output.
+ type Output;
+
+ /// Resolve the value using the style chain.
+ fn resolve(self, styles: StyleChain) -> Self::Output;
+}
+
+impl<T: Resolve> Resolve for Option<T> {
+ type Output = Option<T::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|v| v.resolve(styles))
+ }
+}
+
+impl<T: Resolve> Resolve for Smart<T> {
+ type Output = Smart<T::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|v| v.resolve(styles))
+ }
+}
+
+impl<T: Resolve> Resolve for Spec<T> {
+ type Output = Spec<T::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|v| v.resolve(styles))
+ }
+}
+
+impl<T: Resolve> Resolve for Sides<T> {
+ type Output = Sides<T::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ Sides {
+ left: self.left.resolve(styles),
+ right: self.right.resolve(styles),
+ top: self.top.resolve(styles),
+ bottom: self.bottom.resolve(styles),
+ }
+ }
+}
+
+impl<T> Resolve for Relative<T>
+where
+ T: Resolve + Numeric,
+ <T as Resolve>::Output: Numeric,
+{
+ type Output = Relative<<T as Resolve>::Output>;
+
+ fn resolve(self, styles: StyleChain) -> Self::Output {
+ self.map(|abs| abs.resolve(styles))
+ }
+}
+
/// A show rule recipe.
#[derive(Clone, PartialEq, Hash)]
struct Recipe {
@@ -410,10 +472,10 @@ impl<'a> StyleChain<'a> {
/// Get the output value of a style property.
///
/// Returns the property's default value if no map in the chain contains an
- /// entry for it. Also takes care of folding and returns references where
- /// applicable.
+ /// entry for it. Also takes care of folding and resolving and returns
+ /// references where applicable.
pub fn get<K: Key<'a>>(self, key: K) -> K::Output {
- K::get(self.values(key))
+ K::get(self, self.values(key))
}
/// Execute and return the result of a user recipe for a node if there is
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 44df89e2..12948d72 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -590,6 +590,19 @@ impl<T: Cast> Cast<Spanned<Value>> for Spanned<T> {
}
}
+impl<T: Cast> Cast for Option<T> {
+ fn is(value: &Value) -> bool {
+ matches!(value, Value::None) || T::is(value)
+ }
+
+ fn cast(value: Value) -> StrResult<Self> {
+ match value {
+ Value::None => Ok(None),
+ v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")),
+ }
+ }
+}
+
/// A value that can be automatically determined.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Smart<T> {
@@ -601,6 +614,17 @@ pub enum Smart<T> {
}
impl<T> Smart<T> {
+ /// Map the contained custom value with `f`.
+ pub fn map<F, U>(self, f: F) -> Smart<U>
+ where
+ F: FnOnce(T) -> U,
+ {
+ match self {
+ Self::Auto => Smart::Auto,
+ Self::Custom(x) => Smart::Custom(f(x)),
+ }
+ }
+
/// Returns the contained custom value or a provided default value.
pub fn unwrap_or(self, default: T) -> T {
match self {
@@ -627,19 +651,6 @@ impl<T> Default for Smart<T> {
}
}
-impl<T: Cast> Cast for Option<T> {
- fn is(value: &Value) -> bool {
- matches!(value, Value::None) || T::is(value)
- }
-
- fn cast(value: Value) -> StrResult<Self> {
- match value {
- Value::None => Ok(None),
- v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")),
- }
- }
-}
-
impl<T: Cast> Cast for Smart<T> {
fn is(value: &Value) -> bool {
matches!(value, Value::Auto) || T::is(value)
diff --git a/src/geom/relative.rs b/src/geom/relative.rs
index 8e8897e7..066b8c15 100644
--- a/src/geom/relative.rs
+++ b/src/geom/relative.rs
@@ -34,6 +34,15 @@ impl<T: Numeric> Relative<T> {
pub fn resolve(self, whole: T) -> T {
self.rel.resolve(whole) + self.abs
}
+
+ /// Map the absolute part with `f`.
+ pub fn map<F, U>(self, f: F) -> Relative<U>
+ where
+ F: FnOnce(T) -> U,
+ U: Numeric,
+ {
+ Relative { rel: self.rel, abs: f(self.abs) }
+ }
}
impl<T: Numeric> Debug for Relative<T> {