summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
Diffstat (limited to 'src/library')
-rw-r--r--src/library/elements.rs91
-rw-r--r--src/library/layout.rs151
-rw-r--r--src/library/mod.rs1
-rw-r--r--src/library/text.rs94
-rw-r--r--src/library/utility.rs96
5 files changed, 207 insertions, 226 deletions
diff --git a/src/library/elements.rs b/src/library/elements.rs
index afd7444e..3d318d36 100644
--- a/src/library/elements.rs
+++ b/src/library/elements.rs
@@ -8,46 +8,44 @@ use crate::layout::{
};
/// `image`: An image.
-pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let path = args.expect::<Spanned<EcoString>>(ctx, "path to image file");
- let width = args.named(ctx, "width");
- let height = args.named(ctx, "height");
-
- let mut node = None;
- if let Some(path) = &path {
- if let Some(file) = ctx.resolve(&path.v, path.span) {
- if let Some(id) = ctx.images.load(file) {
- node = Some(ImageNode { id, width, height });
- } else {
- ctx.diag(error!(path.span, "failed to load image"));
- }
- }
- }
-
- Value::template(move |ctx| {
- if let Some(node) = node {
- ctx.push_into_par(node);
- }
- })
+pub fn image(ctx: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let path = args.expect::<Spanned<EcoString>>("path to image file")?;
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+
+ let file = ctx.resolve(&path.v, path.span)?;
+ let node = match ctx.images.load(file) {
+ Some(id) => ImageNode { id, width, height },
+ None => bail!(args.file, path.span, "failed to load image"),
+ };
+
+ Ok(Value::template(move |ctx| ctx.push_into_par(node)))
}
/// `rect`: A rectangle with optional content.
-pub fn rect(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let width = args.named(ctx, "width");
- let height = args.named(ctx, "height");
- let fill = args.named(ctx, "fill");
+pub fn rect(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ let fill = args.named("fill")?;
let body = args.eat().unwrap_or_default();
- rect_impl(width, height, None, fill, body)
+ Ok(rect_impl(width, height, None, fill, body))
}
/// `square`: A square with optional content.
-pub fn square(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let length = args.named::<Length>(ctx, "length").map(Linear::from);
- let width = length.or_else(|| args.named(ctx, "width"));
- let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
- let fill = args.named(ctx, "fill");
+pub fn square(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let length = args.named::<Length>("length")?.map(Linear::from);
+ let width = match length {
+ Some(length) => Some(length),
+ None => args.named("width")?,
+ };
+ let height = match width {
+ Some(_) => None,
+ None => args.named("height")?,
+ };
+ let aspect = Some(N64::from(1.0));
+ let fill = args.named("fill")?;
let body = args.eat().unwrap_or_default();
- rect_impl(width, height, Some(N64::from(1.0)), fill, body)
+ Ok(rect_impl(width, height, aspect, fill, body))
}
fn rect_impl(
@@ -76,22 +74,29 @@ fn rect_impl(
}
/// `ellipse`: An ellipse with optional content.
-pub fn ellipse(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let width = args.named(ctx, "width");
- let height = args.named(ctx, "height");
- let fill = args.named(ctx, "fill");
+pub fn ellipse(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ let fill = args.named("fill")?;
let body = args.eat().unwrap_or_default();
- ellipse_impl(width, height, None, fill, body)
+ Ok(ellipse_impl(width, height, None, fill, body))
}
/// `circle`: A circle with optional content.
-pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let diameter = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
- let width = diameter.or_else(|| args.named(ctx, "width"));
- let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
- let fill = args.named(ctx, "fill");
+pub fn circle(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let diameter = args.named("radius")?.map(|r: Length| 2.0 * Linear::from(r));
+ let width = match diameter {
+ None => args.named("width")?,
+ diameter => diameter,
+ };
+ let height = match width {
+ None => args.named("height")?,
+ width => width,
+ };
+ let aspect = Some(N64::from(1.0));
+ let fill = args.named("fill")?;
let body = args.eat().unwrap_or_default();
- ellipse_impl(width, height, Some(N64::from(1.0)), fill, body)
+ Ok(ellipse_impl(width, height, aspect, fill, body))
}
fn ellipse_impl(
diff --git a/src/library/layout.rs b/src/library/layout.rs
index f3f3f81e..727bbcc3 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -3,26 +3,26 @@ use crate::layout::{FixedNode, GridNode, PadNode, StackChild, StackNode, TrackSi
use crate::paper::{Paper, PaperClass};
/// `page`: Configure pages.
-pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let span = args.span;
- let paper = args.eat::<Spanned<EcoString>>().and_then(|name| {
- Paper::from_name(&name.v).or_else(|| {
- ctx.diag(error!(name.span, "invalid paper name"));
- None
- })
- });
-
- let width = args.named(ctx, "width");
- let height = args.named(ctx, "height");
- let margins = args.named(ctx, "margins");
- let left = args.named(ctx, "left");
- let top = args.named(ctx, "top");
- let right = args.named(ctx, "right");
- let bottom = args.named(ctx, "bottom");
- let flip = args.named(ctx, "flip");
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
-
- Value::template(move |ctx| {
+pub fn page(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let paper = match args.eat::<Spanned<EcoString>>() {
+ Some(name) => match Paper::from_name(&name.v) {
+ None => bail!(args.file, name.span, "invalid paper name"),
+ paper => paper,
+ },
+ None => None,
+ };
+
+ let width = args.named("width")?;
+ let height = args.named("height")?;
+ let margins = args.named("margins")?;
+ let left = args.named("left")?;
+ let top = args.named("top")?;
+ let right = args.named("right")?;
+ let bottom = args.named("bottom")?;
+ let flip = args.named("flip")?;
+ let body = args.expect::<Template>("body")?;
+
+ Ok(Value::template(move |ctx| {
let snapshot = ctx.state.clone();
let state = ctx.state.page_mut();
@@ -65,50 +65,45 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
std::mem::swap(&mut state.size.width, &mut state.size.height);
}
- ctx.pagebreak(false, true, span);
+ ctx.pagebreak(false, true);
body.exec(ctx);
ctx.state = snapshot;
- ctx.pagebreak(true, false, span);
- })
+ ctx.pagebreak(true, false);
+ }))
}
/// `pagebreak`: Start a new page.
-pub fn pagebreak(_: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let span = args.span;
- Value::template(move |ctx| {
- ctx.pagebreak(true, true, span);
- })
+pub fn pagebreak(_: &mut EvalContext, _: &mut FuncArgs) -> TypResult<Value> {
+ Ok(Value::template(move |ctx| ctx.pagebreak(true, true)))
}
/// `h`: Horizontal spacing.
-pub fn h(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- spacing_impl(ctx, args, GenAxis::Cross)
+pub fn h(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ spacing_impl(args, GenAxis::Cross)
}
/// `v`: Vertical spacing.
-pub fn v(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- spacing_impl(ctx, args, GenAxis::Main)
+pub fn v(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ spacing_impl(args, GenAxis::Main)
}
-fn spacing_impl(ctx: &mut EvalContext, args: &mut FuncArgs, axis: GenAxis) -> Value {
- let spacing: Option<Linear> = args.expect(ctx, "spacing");
- Value::template(move |ctx| {
- if let Some(linear) = spacing {
- // TODO: Should this really always be font-size relative?
- let amount = linear.resolve(ctx.state.font.size);
- ctx.push_spacing(axis, amount);
- }
- })
+fn spacing_impl(args: &mut FuncArgs, axis: GenAxis) -> TypResult<Value> {
+ let spacing = args.expect::<Linear>("spacing")?;
+ Ok(Value::template(move |ctx| {
+ // TODO: Should this really always be font-size relative?
+ let amount = spacing.resolve(ctx.state.font.size);
+ ctx.push_spacing(axis, amount);
+ }))
}
/// `align`: Configure the alignment along the layouting axes.
-pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+pub fn align(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let mut horizontal = args.named("horizontal")?;
+ let mut vertical = args.named("vertical")?;
let first = args.eat::<Align>();
let second = args.eat::<Align>();
- let mut horizontal = args.named(ctx, "horizontal");
- let mut vertical = args.named(ctx, "vertical");
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>("body")?;
for value in first.into_iter().chain(second) {
match value.axis() {
@@ -122,7 +117,7 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
}
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
if let Some(horizontal) = horizontal {
ctx.state.aligns.cross = horizontal;
}
@@ -133,37 +128,37 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
body.exec(ctx);
- })
+ }))
}
/// `box`: Place content in a rectangular box.
-pub fn boxed(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let width = args.named(ctx, "width");
- let height = args.named(ctx, "height");
+pub fn boxed(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let width = args.named("width")?;
+ let height = args.named("height")?;
let body = args.eat().unwrap_or_default();
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let child = ctx.exec_template_stack(&body).into();
ctx.push_into_par(FixedNode { width, height, child });
- })
+ }))
}
/// `block`: Place content in a block.
-pub fn block(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let body = args.expect(ctx, "body").unwrap_or_default();
- Value::template(move |ctx| {
+pub fn block(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let body = args.expect("body")?;
+ Ok(Value::template(move |ctx| {
let block = ctx.exec_template_stack(&body);
ctx.push_into_stack(block);
- })
+ }))
}
/// `pad`: Pad content at the sides.
-pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+pub fn pad(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
let all = args.eat();
- let left = args.named(ctx, "left");
- let top = args.named(ctx, "top");
- let right = args.named(ctx, "right");
- let bottom = args.named(ctx, "bottom");
- let body = args.expect(ctx, "body").unwrap_or_default();
+ let left = args.named("left")?;
+ let top = args.named("top")?;
+ let right = args.named("right")?;
+ let bottom = args.named("bottom")?;
+ let body = args.expect("body")?;
let padding = Sides::new(
left.or(all).unwrap_or_default(),
@@ -172,18 +167,18 @@ pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
bottom.or(all).unwrap_or_default(),
);
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let child = ctx.exec_template_stack(&body).into();
ctx.push_into_stack(PadNode { padding, child });
- })
+ }))
}
/// `stack`: Stack children along an axis.
-pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let dir = args.named(ctx, "dir");
+pub fn stack(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let dir = args.named("dir")?;
let children: Vec<_> = args.all().collect();
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let children = children
.iter()
.map(|child| {
@@ -201,23 +196,23 @@ pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
ctx.push_into_stack(StackNode { dirs, aspect: None, children });
- })
+ }))
}
/// `grid`: Arrange children into a grid.
-pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let columns = args.named(ctx, "columns").unwrap_or_default();
- let rows = args.named(ctx, "rows").unwrap_or_default();
+pub fn grid(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let columns = args.named("columns")?.unwrap_or_default();
+ let rows = args.named("rows")?.unwrap_or_default();
- let gutter_columns = args.named(ctx, "gutter-columns");
- let gutter_rows = args.named(ctx, "gutter-rows");
+ let gutter_columns = args.named("gutter-columns")?;
+ let gutter_rows = args.named("gutter-rows")?;
let default = args
- .named(ctx, "gutter")
+ .named("gutter")?
.map(|v| vec![TrackSizing::Linear(v)])
.unwrap_or_default();
- let column_dir = args.named(ctx, "column-dir");
- let row_dir = args.named(ctx, "row-dir");
+ let column_dir = args.named("column-dir")?;
+ let row_dir = args.named("row-dir")?;
let children: Vec<_> = args.all().collect();
@@ -227,7 +222,7 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
gutter_rows.unwrap_or(default),
);
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let children = children
.iter()
.map(|child| ctx.exec_template_stack(child).into())
@@ -257,7 +252,7 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
gutter: gutter.clone(),
children,
})
- })
+ }))
}
/// Defines size of rows and columns in a grid.
diff --git a/src/library/mod.rs b/src/library/mod.rs
index ff2fb3e8..3591e742 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -17,6 +17,7 @@ use std::convert::TryFrom;
use std::rc::Rc;
use crate::color::{Color, RgbaColor};
+use crate::diag::TypResult;
use crate::eval::{EvalContext, FuncArgs, Scope, Template, Type, Value};
use crate::exec::Exec;
use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
diff --git a/src/library/text.rs b/src/library/text.rs
index 54e9794a..5973a7e2 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -4,29 +4,27 @@ use crate::layout::Paint;
use super::*;
/// `font`: Configure the font.
-pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let size = args.eat::<Linear>().or_else(|| args.named(ctx, "size"));
- let style = args.named(ctx, "style");
- let weight = args.named(ctx, "weight");
- let stretch = args.named(ctx, "stretch");
- let top_edge = args.named(ctx, "top-edge");
- let bottom_edge = args.named(ctx, "bottom-edge");
- let fill = args.named(ctx, "fill");
-
- let families: Vec<_> = args.all().collect();
- let list = if families.is_empty() {
- args.named(ctx, "family")
- } else {
- Some(FontDef(Rc::new(families)))
- };
+pub fn font(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let size = args.named::<Linear>("size")?.or_else(|| args.eat());
+ let style = args.named("style")?;
+ let weight = args.named("weight")?;
+ let stretch = args.named("stretch")?;
+ let top_edge = args.named("top-edge")?;
+ let bottom_edge = args.named("bottom-edge")?;
+ let fill = args.named("fill")?;
+
+ let list = args.named("family")?.or_else(|| {
+ let families: Vec<_> = args.all().collect();
+ (!families.is_empty()).then(|| FontDef(Rc::new(families)))
+ });
- let serif = args.named(ctx, "serif");
- let sans_serif = args.named(ctx, "sans-serif");
- let monospace = args.named(ctx, "monospace");
+ let serif = args.named("serif")?;
+ let sans_serif = args.named("sans-serif")?;
+ let monospace = args.named("monospace")?;
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>("body")?;
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let state = ctx.state.font_mut();
if let Some(size) = size {
@@ -74,7 +72,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
body.exec(ctx);
- })
+ }))
}
struct FontDef(Rc<Vec<FontFamily>>);
@@ -106,12 +104,12 @@ castable! {
}
/// `par`: Configure paragraphs.
-pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- let par_spacing = args.named(ctx, "spacing");
- let line_spacing = args.named(ctx, "leading");
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
+pub fn par(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let par_spacing = args.named("spacing")?;
+ let line_spacing = args.named("leading")?;
+ let body = args.expect::<Template>("body")?;
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
let state = ctx.state.par_mut();
if let Some(par_spacing) = par_spacing {
@@ -124,33 +122,32 @@ pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
ctx.parbreak();
body.exec(ctx);
- })
+ }))
}
/// `lang`: Configure the language.
-pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
+pub fn lang(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
let iso = args.eat::<EcoString>();
- let dir = if let Some(dir) = args.named::<Spanned<Dir>>(ctx, "dir") {
+ let dir = if let Some(dir) = args.named::<Spanned<Dir>>("dir")? {
if dir.v.axis() == SpecAxis::Horizontal {
Some(dir.v)
} else {
- ctx.diag(error!(dir.span, "must be horizontal"));
- None
+ bail!(args.file, dir.span, "must be horizontal");
}
} else {
iso.as_deref().map(lang_dir)
};
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>("body")?;
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
if let Some(dir) = dir {
ctx.state.dirs.cross = dir;
}
ctx.parbreak();
body.exec(ctx);
- })
+ }))
}
/// The default direction for the language identified by the given `iso` code.
@@ -163,30 +160,29 @@ fn lang_dir(iso: &str) -> Dir {
}
/// `strike`: Enable striken-through text.
-pub fn strike(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- line_impl(ctx, args, |font| &mut font.strikethrough)
+pub fn strike(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ line_impl(args, |font| &mut font.strikethrough)
}
/// `underline`: Enable underlined text.
-pub fn underline(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- line_impl(ctx, args, |font| &mut font.underline)
+pub fn underline(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ line_impl(args, |font| &mut font.underline)
}
/// `overline`: Add an overline above text.
-pub fn overline(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- line_impl(ctx, args, |font| &mut font.overline)
+pub fn overline(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ line_impl(args, |font| &mut font.overline)
}
fn line_impl(
- ctx: &mut EvalContext,
args: &mut FuncArgs,
substate: fn(&mut FontState) -> &mut Option<Rc<LineState>>,
-) -> Value {
- let stroke = args.eat().or_else(|| args.named(ctx, "stroke"));
- let thickness = args.eat::<Linear>().or_else(|| args.named(ctx, "thickness"));
- let offset = args.named(ctx, "offset");
- let extent = args.named(ctx, "extent").unwrap_or_default();
- let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
+) -> TypResult<Value> {
+ let stroke = args.named("stroke")?.or_else(|| args.eat());
+ let thickness = args.named::<Linear>("thickness")?.or_else(|| args.eat());
+ let offset = args.named("offset")?;
+ let extent = args.named("extent")?.unwrap_or_default();
+ let body = args.expect::<Template>("body")?;
// Suppress any existing strikethrough if strength is explicitly zero.
let state = thickness.map_or(true, |s| !s.is_zero()).then(|| {
@@ -198,8 +194,8 @@ fn line_impl(
})
});
- Value::template(move |ctx| {
+ Ok(Value::template(move |ctx| {
*substate(ctx.state.font_mut()) = state.clone();
body.exec(ctx);
- })
+ }))
}
diff --git a/src/library/utility.rs b/src/library/utility.rs
index 4001d14f..fb39fce3 100644
--- a/src/library/utility.rs
+++ b/src/library/utility.rs
@@ -7,94 +7,78 @@ use crate::pretty::pretty;
use super::*;
/// `type`: The name of a value's type.
-pub fn type_(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- match args.expect::<Value>(ctx, "value") {
- Some(value) => value.type_name().into(),
- None => Value::Error,
- }
+pub fn type_(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let value = args.expect::<Value>("value")?;
+ Ok(value.type_name().into())
}
/// `repr`: The string representation of a value.
-pub fn repr(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- match args.expect::<Value>(ctx, "value") {
- Some(value) => pretty(&value).into(),
- None => Value::Error,
- }
+pub fn repr(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let value = args.expect::<Value>("value")?;
+ Ok(pretty(&value).into())
}
/// `len`: The length of a string, an array or a dictionary.
-pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- match args.expect(ctx, "collection") {
- Some(Spanned { v: Value::Str(v), .. }) => Value::Int(v.len() as i64),
- Some(Spanned { v: Value::Array(v), .. }) => Value::Int(v.len() as i64),
- Some(Spanned { v: Value::Dict(v), .. }) => Value::Int(v.len() as i64),
- Some(other) if other.v != Value::Error => {
- ctx.diag(error!(other.span, "expected string, array or dictionary"));
- Value::Error
- }
- _ => Value::Error,
- }
+pub fn len(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ let Spanned { v, span } = args.expect("collection")?;
+ Ok(match v {
+ Value::Str(v) => Value::Int(v.len() as i64),
+ Value::Array(v) => Value::Int(v.len() as i64),
+ Value::Dict(v) => Value::Int(v.len() as i64),
+ _ => bail!(args.file, span, "expected string, array or dictionary"),
+ })
}
/// `rgb`: Create an RGB(A) color.
-pub fn rgb(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- Value::Color(Color::Rgba(
+pub fn rgb(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ Ok(Value::Color(Color::Rgba(
if let Some(string) = args.eat::<Spanned<EcoString>>() {
match RgbaColor::from_str(&string.v) {
Ok(color) => color,
- Err(_) => {
- ctx.diag(error!(string.span, "invalid color"));
- return Value::Error;
- }
+ Err(_) => bail!(args.file, string.span, "invalid color"),
}
} else {
- let r = args.expect(ctx, "red component").unwrap_or(0.0);
- let g = args.expect(ctx, "green component").unwrap_or(0.0);
- let b = args.expect(ctx, "blue component").unwrap_or(0.0);
+ let r = args.expect("red component")?;
+ let g = args.expect("green component")?;
+ let b = args.expect("blue component")?;
let a = args.eat().unwrap_or(1.0);
let f = |v: f64| (v.clamp(0.0, 1.0) * 255.0).round() as u8;
RgbaColor::new(f(r), f(g), f(b), f(a))
},
- ))
+ )))
}
/// `min`: The minimum of a sequence of values.
-pub fn min(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- minmax(ctx, args, Ordering::Less)
+pub fn min(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ minmax(args, Ordering::Less)
}
/// `max`: The maximum of a sequence of values.
-pub fn max(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
- minmax(ctx, args, Ordering::Greater)
+pub fn max(_: &mut EvalContext, args: &mut FuncArgs) -> TypResult<Value> {
+ minmax(args, Ordering::Greater)
}
/// Find the minimum or maximum of a sequence of values.
-fn minmax(ctx: &mut EvalContext, args: &mut FuncArgs, goal: Ordering) -> Value {
- let span = args.span;
- let mut extremum = None;
+fn minmax(args: &mut FuncArgs, goal: Ordering) -> TypResult<Value> {
+ let &mut FuncArgs { file, span, .. } = args;
+ let mut extremum = args.expect::<Value>("value")?;
for value in args.all::<Value>() {
- if let Some(prev) = &extremum {
- match value.partial_cmp(&prev) {
- Some(ordering) if ordering == goal => extremum = Some(value),
- Some(_) => {}
- None => {
- ctx.diag(error!(
- span,
- "cannot compare {} with {}",
- prev.type_name(),
- value.type_name(),
- ));
- return Value::Error;
+ match value.partial_cmp(&extremum) {
+ Some(ordering) => {
+ if ordering == goal {
+ extremum = value;
}
}
- } else {
- extremum = Some(value);
+ None => bail!(
+ file,
+ span,
+ "cannot compare {} with {}",
+ extremum.type_name(),
+ value.type_name(),
+ ),
}
}
- extremum.unwrap_or_else(|| {
- args.expect::<Value>(ctx, "value");
- Value::Error
- })
+ Ok(extremum)
}