Proper error messages for shorthands
This commit is contained in:
parent
6e198bf760
commit
624471db61
@ -56,20 +56,23 @@ fn expand(mut impl_block: syn::ItemImpl) -> Result<TokenStream2> {
|
||||
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! {
|
||||
|
@ -162,10 +162,7 @@ impl Args {
|
||||
///
|
||||
/// Returns a `missing argument: {what}` error if no positional argument is
|
||||
/// left.
|
||||
pub fn expect<T>(&mut self, what: &str) -> TypResult<T>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
pub fn expect<T: Cast>(&mut self, what: &str) -> TypResult<T> {
|
||||
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<T>(&mut self) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
pub fn eat<T: Cast>(&mut self) -> TypResult<Option<T>> {
|
||||
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<T>(&mut self) -> Option<T>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
pub fn find<T: Cast>(&mut self) -> TypResult<Option<T>> {
|
||||
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<T>(&mut self) -> impl Iterator<Item = T> + '_
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
std::iter::from_fn(move || self.find())
|
||||
pub fn all<T: Cast>(&mut self) -> TypResult<Vec<T>> {
|
||||
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<T>(&mut self, name: &str) -> TypResult<Option<T>>
|
||||
where
|
||||
T: Cast<Spanned<Value>>,
|
||||
{
|
||||
pub fn named<T: Cast>(&mut self, name: &str) -> TypResult<Option<T>> {
|
||||
// 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<T: Cast>(&mut self, name: &str) -> TypResult<Option<T>> {
|
||||
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 {
|
||||
|
@ -284,7 +284,7 @@ pub trait Type {
|
||||
}
|
||||
|
||||
/// Cast from a value to a specific type.
|
||||
pub trait Cast<V>: Sized {
|
||||
pub trait Cast<V = Spanned<Value>>: Sized {
|
||||
/// Check whether the value is castable to `Self`.
|
||||
fn is(value: &V) -> bool;
|
||||
|
||||
@ -415,7 +415,7 @@ impl Cast<Value> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cast<Spanned<Value>> for T
|
||||
impl<T> Cast for T
|
||||
where
|
||||
T: Cast<Value>,
|
||||
{
|
||||
@ -428,7 +428,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Cast<Spanned<Value>> for Spanned<T>
|
||||
impl<T> Cast for Spanned<T>
|
||||
where
|
||||
T: Cast<Value>,
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ pub struct AlignNode {
|
||||
#[class]
|
||||
impl AlignNode {
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
let aligns: Spec<_> = args.find().unwrap_or_default();
|
||||
let aligns: Spec<_> = args.find()?.unwrap_or_default();
|
||||
let body: LayoutNode = args.expect("body")?;
|
||||
Ok(Template::block(body.aligned(aligns)))
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ impl BoxNode {
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
let width = args.named("width")?;
|
||||
let height = args.named("height")?;
|
||||
let body: LayoutNode = args.find().unwrap_or_default();
|
||||
let body: LayoutNode = args.find()?.unwrap_or_default();
|
||||
Ok(Template::inline(body.sized(Spec::new(width, height))))
|
||||
}
|
||||
}
|
||||
@ -21,6 +21,6 @@ pub struct BlockNode;
|
||||
#[class]
|
||||
impl BlockNode {
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
Ok(Template::Block(args.find().unwrap_or_default()))
|
||||
Ok(Template::Block(args.find()?.unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ impl GridNode {
|
||||
column_gutter.unwrap_or_else(|| base_gutter.clone()),
|
||||
row_gutter.unwrap_or(base_gutter),
|
||||
),
|
||||
children: args.all().collect(),
|
||||
children: args.all()?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl LinkNode {
|
||||
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
let url = args.expect::<EcoString>("url")?;
|
||||
let body = args.find().unwrap_or_else(|| {
|
||||
let body = args.find()?.unwrap_or_else(|| {
|
||||
let mut text = url.as_str();
|
||||
for prefix in ["mailto:", "tel:"] {
|
||||
text = text.trim_start_matches(prefix);
|
||||
|
@ -21,7 +21,8 @@ impl<L: ListLabel> ListNode<L> {
|
||||
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
Ok(args
|
||||
.all()
|
||||
.all()?
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, child)| Template::show(Self { label: L::new(1 + i), child }))
|
||||
.sum())
|
||||
|
@ -14,7 +14,7 @@ pub struct PadNode {
|
||||
#[class]
|
||||
impl PadNode {
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
let all = args.find();
|
||||
let all = args.find()?;
|
||||
let left = args.named("left")?;
|
||||
let top = args.named("top")?;
|
||||
let right = args.named("right")?;
|
||||
|
@ -36,7 +36,7 @@ impl PageNode {
|
||||
}
|
||||
|
||||
fn set(args: &mut Args, styles: &mut StyleMap) -> TypResult<()> {
|
||||
if let Some(paper) = args.named::<Paper>("paper")?.or_else(|| args.find()) {
|
||||
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
|
||||
styles.set(Self::WIDTH, Smart::Custom(paper.width()));
|
||||
styles.set(Self::HEIGHT, Smart::Custom(paper.height()));
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ pub struct PlaceNode(pub LayoutNode);
|
||||
#[class]
|
||||
impl PlaceNode {
|
||||
fn construct(_: &mut EvalContext, args: &mut Args) -> TypResult<Template> {
|
||||
let aligns = args.find().unwrap_or(Spec::with_x(Some(Align::Left)));
|
||||
let aligns = args.find()?.unwrap_or(Spec::with_x(Some(Align::Left)));
|
||||
let tx = args.named("dx")?.unwrap_or_default();
|
||||
let ty = args.named("dy")?.unwrap_or_default();
|
||||
let body: LayoutNode = args.expect("body")?;
|
||||
|
@ -45,7 +45,7 @@ impl<S: ShapeKind> ShapeNode<S> {
|
||||
};
|
||||
|
||||
Ok(Template::inline(
|
||||
ShapeNode { kind: S::default(), child: args.find() }
|
||||
ShapeNode { kind: S::default(), child: args.find()? }
|
||||
.pack()
|
||||
.sized(Spec::new(width, height)),
|
||||
))
|
||||
|
@ -20,7 +20,7 @@ impl StackNode {
|
||||
Ok(Template::block(Self {
|
||||
dir: args.named("dir")?.unwrap_or(Dir::TTB),
|
||||
spacing: args.named("spacing")?,
|
||||
children: args.all().collect(),
|
||||
children: args.all()?,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ impl TableNode {
|
||||
column_gutter.unwrap_or_else(|| base_gutter.clone()),
|
||||
row_gutter.unwrap_or(base_gutter),
|
||||
),
|
||||
children: args.all().collect(),
|
||||
children: args.all()?,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -78,9 +78,7 @@ pub struct Rotate(pub Angle);
|
||||
|
||||
impl TransformKind for Rotate {
|
||||
fn construct(args: &mut Args) -> TypResult<Self> {
|
||||
Ok(Self(
|
||||
args.named("angle")?.or_else(|| args.find()).unwrap_or_default(),
|
||||
))
|
||||
Ok(Self(args.named_or_find("angle")?.unwrap_or_default()))
|
||||
}
|
||||
|
||||
fn matrix(&self) -> Transform {
|
||||
@ -94,7 +92,7 @@ pub struct Scale(pub Relative, pub Relative);
|
||||
|
||||
impl TransformKind for Scale {
|
||||
fn construct(args: &mut Args) -> TypResult<Self> {
|
||||
let all = args.find();
|
||||
let all = args.find()?;
|
||||
let sx = args.named("x")?.or(all).unwrap_or(Relative::one());
|
||||
let sy = args.named("y")?.or(all).unwrap_or(Relative::one());
|
||||
Ok(Self(sx, sy))
|
||||
|
@ -31,7 +31,7 @@ pub fn join(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let sep = args.named::<Value>("sep")?.unwrap_or(Value::None);
|
||||
|
||||
let mut result = Value::None;
|
||||
let mut iter = args.all::<Value>();
|
||||
let mut iter = args.all::<Value>()?.into_iter();
|
||||
|
||||
if let Some(first) = iter.next() {
|
||||
result = first;
|
||||
@ -88,7 +88,7 @@ pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
/// Create an RGB(A) color.
|
||||
pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::from(
|
||||
if let Some(string) = args.find::<Spanned<EcoString>>() {
|
||||
if let Some(string) = args.find::<Spanned<EcoString>>()? {
|
||||
match RgbaColor::from_str(&string.v) {
|
||||
Ok(color) => color,
|
||||
Err(_) => bail!(string.span, "invalid hex string"),
|
||||
@ -208,7 +208,7 @@ pub fn modulo(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
/// Find the minimum or maximum of a sequence of values.
|
||||
fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> {
|
||||
let mut extremum = args.expect::<Value>("value")?;
|
||||
for Spanned { v, span } in args.all::<Spanned<Value>>() {
|
||||
for Spanned { v, span } in args.all::<Spanned<Value>>()? {
|
||||
match v.partial_cmp(&extremum) {
|
||||
Some(ordering) => {
|
||||
if ordering == goal {
|
||||
|
Loading…
x
Reference in New Issue
Block a user