From e1d7edb7c1845e6df6f5e23e3baf7bc88159eade Mon Sep 17 00:00:00 2001 From: Laurenz Date: Fri, 8 Apr 2022 14:48:02 +0200 Subject: [PATCH] Property resolving --- macros/src/lib.rs | 28 ++++++++++++----- src/eval/styles.rs | 72 +++++++++++++++++++++++++++++++++++++++++--- src/eval/value.rs | 37 +++++++++++++++-------- src/geom/relative.rs | 9 ++++++ 4 files changed, 121 insertions(+), 25 deletions(-) diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 09739b05b..429c5b09b 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) -> Self::Output { + fn get( + chain: StyleChain<'a>, + mut values: impl Iterator, + ) -> 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 { 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 { "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 { )); } - 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 b7f1889c4..575518f59 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) -> Self::Output; + fn get( + chain: StyleChain<'a>, + values: impl Iterator, + ) -> 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 Resolve for Option { + type Output = Option; + + fn resolve(self, styles: StyleChain) -> Self::Output { + self.map(|v| v.resolve(styles)) + } +} + +impl Resolve for Smart { + type Output = Smart; + + fn resolve(self, styles: StyleChain) -> Self::Output { + self.map(|v| v.resolve(styles)) + } +} + +impl Resolve for Spec { + type Output = Spec; + + fn resolve(self, styles: StyleChain) -> Self::Output { + self.map(|v| v.resolve(styles)) + } +} + +impl Resolve for Sides { + type Output = Sides; + + 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 Resolve for Relative +where + T: Resolve + Numeric, + ::Output: Numeric, +{ + type Output = Relative<::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>(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 44df89e20..12948d72f 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -590,6 +590,19 @@ impl Cast> for Spanned { } } +impl Cast for Option { + fn is(value: &Value) -> bool { + matches!(value, Value::None) || T::is(value) + } + + fn cast(value: Value) -> StrResult { + 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 { @@ -601,6 +614,17 @@ pub enum Smart { } impl Smart { + /// Map the contained custom value with `f`. + pub fn map(self, f: F) -> Smart + 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 Default for Smart { } } -impl Cast for Option { - fn is(value: &Value) -> bool { - matches!(value, Value::None) || T::is(value) - } - - fn cast(value: Value) -> StrResult { - match value { - Value::None => Ok(None), - v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")), - } - } -} - impl Cast for Smart { 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 8e8897e77..066b8c15d 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -34,6 +34,15 @@ impl Relative { pub fn resolve(self, whole: T) -> T { self.rel.resolve(whole) + self.abs } + + /// Map the absolute part with `f`. + pub fn map(self, f: F) -> Relative + where + F: FnOnce(T) -> U, + U: Numeric, + { + Relative { rel: self.rel, abs: f(self.abs) } + } } impl Debug for Relative {