diff options
Diffstat (limited to 'src/library')
| -rw-r--r-- | src/library/elements.rs | 91 | ||||
| -rw-r--r-- | src/library/layout.rs | 151 | ||||
| -rw-r--r-- | src/library/mod.rs | 1 | ||||
| -rw-r--r-- | src/library/text.rs | 94 | ||||
| -rw-r--r-- | src/library/utility.rs | 96 |
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) } |
