summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-05-06 10:05:11 +0200
committerLaurenz <laurmaedje@gmail.com>2022-05-06 10:05:11 +0200
commitbfaf5447a789cd0dbbb1e418bea62fef9edc2b7d (patch)
tree0cc1a47b37439fbeda06c57ebef0025becae0066 /src
parent49b8574b8d03e52a990f7d7b009c36fbdad0d55a (diff)
Cast content from string
Diffstat (limited to 'src')
-rw-r--r--src/eval/args.rs28
-rw-r--r--src/eval/func.rs9
-rw-r--r--src/eval/value.rs12
-rw-r--r--src/library/graphics/shape.rs21
-rw-r--r--src/library/layout/container.rs4
-rw-r--r--src/library/layout/pad.rs2
-rw-r--r--src/library/layout/page.rs15
-rw-r--r--src/library/text/link.rs2
-rw-r--r--src/library/text/mod.rs44
9 files changed, 76 insertions, 61 deletions
diff --git a/src/eval/args.rs b/src/eval/args.rs
index f507e714..9b21cfa2 100644
--- a/src/eval/args.rs
+++ b/src/eval/args.rs
@@ -44,20 +44,6 @@ impl Args {
Self { span, items }
}
- /// Consume and cast the first positional argument.
- ///
- /// 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>>,
- {
- match self.eat()? {
- Some(v) => Ok(v),
- None => bail!(self.span, "missing argument: {}", what),
- }
- }
-
/// Consume and cast the first positional argument if there is one.
pub fn eat<T>(&mut self) -> TypResult<Option<T>>
where
@@ -73,6 +59,20 @@ impl Args {
Ok(None)
}
+ /// Consume and cast the first positional argument.
+ ///
+ /// 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>>,
+ {
+ match self.eat()? {
+ Some(v) => Ok(v),
+ None => bail!(self.span, "missing argument: {}", what),
+ }
+ }
+
/// Find and consume the first castable positional argument.
pub fn find<T>(&mut self) -> TypResult<Option<T>>
where
diff --git a/src/eval/func.rs b/src/eval/func.rs
index b72e9f18..73f2cac9 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -43,11 +43,11 @@ impl Func {
Self(Arc::new(Repr::Native(Native {
name,
func: |ctx, args| {
- let styles = T::set(args)?;
+ let styles = T::set(args, true)?;
let content = T::construct(ctx, args)?;
Ok(Value::Content(content.styled_with_map(styles.scoped())))
},
- set: Some(T::set),
+ set: Some(|args| T::set(args, false)),
node: T::SHOWABLE.then(|| NodeId::of::<T>()),
})))
}
@@ -165,7 +165,10 @@ pub trait Node: 'static {
fn construct(ctx: &mut Context, args: &mut Args) -> TypResult<Content>;
/// Parse the arguments into style properties for this node.
- fn set(args: &mut Args) -> TypResult<StyleMap>;
+ ///
+ /// When `constructor` is true, [`construct`](Self::construct) will run
+ /// after this invocation of `set`.
+ fn set(args: &mut Args, constructor: bool) -> TypResult<StyleMap>;
}
/// A user-defined closure.
diff --git a/src/eval/value.rs b/src/eval/value.rs
index b5607cfd..dd183926 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -464,11 +464,19 @@ primitive! { f64: "float", Float, Int(v) => v as f64 }
primitive! { RawLength: "length", Length }
primitive! { Angle: "angle", Angle }
primitive! { Ratio: "ratio", Ratio }
-primitive! { Relative<RawLength>: "relative length", Relative, Length(v) => v.into(), Ratio(v) => v.into() }
+primitive! { Relative<RawLength>: "relative length",
+ Relative,
+ Length(v) => v.into(),
+ Ratio(v) => v.into()
+}
primitive! { Fraction: "fraction", Fraction }
primitive! { Color: "color", Color }
primitive! { EcoString: "string", Str }
-primitive! { Content: "content", Content, None => Content::new() }
+primitive! { Content: "content",
+ Content,
+ None => Content::new(),
+ Str(text) => Content::Text(text)
+}
primitive! { Array: "array", Array }
primitive! { Dict: "dictionary", Dict }
primitive! { Func: "function", Func }
diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs
index 40b6e1e3..bc768628 100644
--- a/src/library/graphics/shape.rs
+++ b/src/library/graphics/shape.rs
@@ -24,19 +24,17 @@ impl<const S: ShapeKind> ShapeNode<S> {
/// How to fill the shape.
pub const FILL: Option<Paint> = None;
/// How to stroke the shape.
- #[property(resolve, fold)]
+ #[property(skip, resolve, fold)]
pub const STROKE: Smart<Sides<Option<RawStroke>>> = Smart::Auto;
/// How much to pad the shape's content.
#[property(resolve, fold)]
pub const INSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
-
/// How much to extend the shape's dimensions beyond the allocated space.
#[property(resolve, fold)]
pub const OUTSET: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
-
/// How much to round the shape's corners.
- #[property(resolve, fold)]
+ #[property(skip, resolve, fold)]
pub const RADIUS: Sides<Option<Relative<RawLength>>> = Sides::splat(Relative::zero());
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
@@ -57,14 +55,11 @@ impl<const S: ShapeKind> ShapeNode<S> {
};
Ok(Content::inline(
- Self(args.find()?).pack().sized(Spec::new(width, height)),
+ Self(args.eat()?).pack().sized(Spec::new(width, height)),
))
}
- fn set(args: &mut Args) -> TypResult<StyleMap> {
- let mut styles = StyleMap::new();
- styles.set_opt(Self::FILL, args.named("fill")?);
-
+ fn set(...) {
if is_round(S) {
styles.set_opt(
Self::STROKE,
@@ -73,16 +68,8 @@ impl<const S: ShapeKind> ShapeNode<S> {
);
} else {
styles.set_opt(Self::STROKE, args.named("stroke")?);
- }
-
- styles.set_opt(Self::INSET, args.named("inset")?);
- styles.set_opt(Self::OUTSET, args.named("outset")?);
-
- if !is_round(S) {
styles.set_opt(Self::RADIUS, args.named("radius")?);
}
-
- Ok(styles)
}
}
diff --git a/src/library/layout/container.rs b/src/library/layout/container.rs
index 6689dd48..5264f258 100644
--- a/src/library/layout/container.rs
+++ b/src/library/layout/container.rs
@@ -8,7 +8,7 @@ impl BoxNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
let width = args.named("width")?;
let height = args.named("height")?;
- let body: LayoutNode = args.find()?.unwrap_or_default();
+ let body: LayoutNode = args.eat()?.unwrap_or_default();
Ok(Content::inline(body.sized(Spec::new(width, height))))
}
}
@@ -19,6 +19,6 @@ pub struct BlockNode;
#[node]
impl BlockNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
- Ok(Content::Block(args.find()?.unwrap_or_default()))
+ Ok(Content::Block(args.eat()?.unwrap_or_default()))
}
}
diff --git a/src/library/layout/pad.rs b/src/library/layout/pad.rs
index 2be21bcb..aff0e8b0 100644
--- a/src/library/layout/pad.rs
+++ b/src/library/layout/pad.rs
@@ -12,7 +12,7 @@ pub struct PadNode {
#[node]
impl PadNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
- let all = args.find()?;
+ let all = args.named("rest")?.or(args.find()?);
let x = args.named("x")?;
let y = args.named("y")?;
let left = args.named("left")?.or(x).or(all).unwrap_or_default();
diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs
index c8495e64..324ac285 100644
--- a/src/library/layout/page.rs
+++ b/src/library/layout/page.rs
@@ -39,24 +39,11 @@ impl PageNode {
Ok(Content::Page(Self(args.expect("body")?)))
}
- fn set(args: &mut Args) -> TypResult<StyleMap> {
- let mut styles = StyleMap::new();
-
+ fn set(...) {
if let Some(paper) = args.named_or_find::<Paper>("paper")? {
styles.set(Self::WIDTH, Smart::Custom(paper.width().into()));
styles.set(Self::HEIGHT, Smart::Custom(paper.height().into()));
}
-
- styles.set_opt(Self::WIDTH, args.named("width")?);
- styles.set_opt(Self::HEIGHT, args.named("height")?);
- styles.set_opt(Self::MARGINS, args.named("margins")?);
- styles.set_opt(Self::FLIPPED, args.named("flipped")?);
- styles.set_opt(Self::FILL, args.named("fill")?);
- styles.set_opt(Self::COLUMNS, args.named("columns")?);
- styles.set_opt(Self::HEADER, args.named("header")?);
- styles.set_opt(Self::FOOTER, args.named("footer")?);
-
- Ok(styles)
}
}
diff --git a/src/library/text/link.rs b/src/library/text/link.rs
index 423bcbfb..9e933529 100644
--- a/src/library/text/link.rs
+++ b/src/library/text/link.rs
@@ -22,7 +22,7 @@ impl LinkNode {
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
Ok(Content::show(Self {
url: args.expect::<EcoString>("url")?,
- body: args.find()?,
+ body: args.eat()?,
}))
}
}
diff --git a/src/library/text/mod.rs b/src/library/text/mod.rs
index 09c7fa17..80b036ac 100644
--- a/src/library/text/mod.rs
+++ b/src/library/text/mod.rs
@@ -35,7 +35,7 @@ pub struct TextNode;
#[node]
impl TextNode {
/// A prioritized sequence of font families.
- #[property(referenced, variadic)]
+ #[property(skip, referenced)]
pub const FAMILY: Vec<FontFamily> = vec![FontFamily::new("IBM Plex Sans")];
/// Whether to allow font fallback when the primary font list contains no
/// match.
@@ -109,22 +109,22 @@ impl TextNode {
pub const FEATURES: Vec<(Tag, u32)> = vec![];
/// Whether the font weight should be increased by 300.
- #[property(hidden, fold)]
+ #[property(skip, fold)]
pub const STRONG: Toggle = false;
/// Whether the the font style should be inverted.
- #[property(hidden, fold)]
+ #[property(skip, fold)]
pub const EMPH: Toggle = false;
/// A case transformation that should be applied to the text.
- #[property(hidden)]
+ #[property(skip)]
pub const CASE: Option<Case> = None;
/// Whether small capital glyphs should be used. ("smcp")
- #[property(hidden)]
+ #[property(skip)]
pub const SMALLCAPS: bool = false;
/// An URL the text should link to.
- #[property(hidden, referenced)]
+ #[property(skip, referenced)]
pub const LINK: Option<EcoString> = None;
/// Decorative lines.
- #[property(hidden, fold)]
+ #[property(skip, fold)]
pub const DECO: Decoration = vec![];
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
@@ -133,6 +133,36 @@ impl TextNode {
// styles all text in it.
args.expect("body")
}
+
+ fn set(...) {
+ if let Some(family) = args.named("family")? {
+ styles.set(Self::FAMILY, family);
+ } else {
+ let mut count = 0;
+ let mut content = false;
+ for item in args.items.iter().filter(|item| item.name.is_none()) {
+ if EcoString::is(&item.value) {
+ count += 1;
+ } else if Content::is(&item.value) {
+ content = true;
+ }
+ }
+
+ // Skip the final string if it's needed as the body.
+ if constructor && !content && count > 0 {
+ count -= 1;
+ }
+
+ if count > 0 {
+ let mut list = Vec::with_capacity(count);
+ for _ in 0 .. count {
+ list.push(args.find()?.unwrap());
+ }
+
+ styles.set(Self::FAMILY, list);
+ }
+ }
+ }
}
/// A font family like "Arial".