diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 0757a201d..ab9fbf1b3 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -56,20 +56,23 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result { let name = property.name; let string = name.to_string().replace("_", "-").to_lowercase(); - let alternative = if property.variadic { + let value = if property.variadic { quote! { - .or_else(|| { - let list: Vec<_> = args.all().collect(); - (!list.is_empty()).then(|| list) - }) + match args.named(#string)? { + Some(value) => value, + None => { + let list: Vec<_> = args.all()?; + (!list.is_empty()).then(|| list) + } + } } } else if property.shorthand { - quote! { .or_else(|| args.find()) } + quote! { args.named_or_find(#string)? } } else { - quote! {} + quote! { args.named(#string)? } }; - quote! { styles.set_opt(Self::#name, args.named(#string)? #alternative); } + quote! { styles.set_opt(Self::#name, #value); } }); parse_quote! { diff --git a/src/eval/func.rs b/src/eval/func.rs index 6d405ca43..887f79897 100644 --- a/src/eval/func.rs +++ b/src/eval/func.rs @@ -162,10 +162,7 @@ impl Args { /// /// Returns a `missing argument: {what}` error if no positional argument is /// left. - pub fn expect(&mut self, what: &str) -> TypResult - where - T: Cast>, - { + pub fn expect(&mut self, what: &str) -> TypResult { match self.eat()? { Some(v) => Ok(v), None => bail!(self.span, "missing argument: {}", what), @@ -173,10 +170,7 @@ impl Args { } /// Consume and cast the first positional argument if there is one. - pub fn eat(&mut self) -> TypResult> - where - T: Cast>, - { + pub fn eat(&mut self) -> TypResult> { for (i, slot) in self.items.iter().enumerate() { if slot.name.is_none() { let value = self.items.remove(i).value; @@ -188,33 +182,29 @@ impl Args { } /// Find and consume the first castable positional argument. - pub fn find(&mut self) -> Option - where - T: Cast>, - { + pub fn find(&mut self) -> TypResult> { for (i, slot) in self.items.iter().enumerate() { if slot.name.is_none() && T::is(&slot.value) { let value = self.items.remove(i).value; - return T::cast(value).ok(); + let span = value.span; + return T::cast(value).at(span).map(Some); } } - None + Ok(None) } /// Find and consume all castable positional arguments. - pub fn all(&mut self) -> impl Iterator + '_ - where - T: Cast>, - { - std::iter::from_fn(move || self.find()) + pub fn all(&mut self) -> TypResult> { + let mut list = vec![]; + while let Some(value) = self.find()? { + list.push(value); + } + Ok(list) } /// Cast and remove the value for the given named argument, returning an /// error if the conversion fails. - pub fn named(&mut self, name: &str) -> TypResult> - where - T: Cast>, - { + pub fn named(&mut self, name: &str) -> TypResult> { // We don't quit once we have a match because when multiple matches // exist, we want to remove all of them and use the last one. let mut i = 0; @@ -231,6 +221,14 @@ impl Args { Ok(found) } + /// Same as named, but with fallback to find. + pub fn named_or_find(&mut self, name: &str) -> TypResult> { + match self.named(name)? { + Some(value) => Ok(Some(value)), + None => self.find(), + } + } + /// Take out all arguments into a new instance. pub fn take(&mut self) -> Self { Self { diff --git a/src/eval/value.rs b/src/eval/value.rs index 2d37b34f2..d1f0be767 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -284,7 +284,7 @@ pub trait Type { } /// Cast from a value to a specific type. -pub trait Cast: Sized { +pub trait Cast>: Sized { /// Check whether the value is castable to `Self`. fn is(value: &V) -> bool; @@ -415,7 +415,7 @@ impl Cast for Value { } } -impl Cast> for T +impl Cast for T where T: Cast, { @@ -428,7 +428,7 @@ where } } -impl Cast> for Spanned +impl Cast for Spanned where T: Cast, { diff --git a/src/library/align.rs b/src/library/align.rs index 5dc129386..1fb911388 100644 --- a/src/library/align.rs +++ b/src/library/align.rs @@ -15,7 +15,7 @@ pub struct AlignNode { #[class] impl AlignNode { fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult