diff options
Diffstat (limited to 'src/eval/cast.rs')
| -rw-r--r-- | src/eval/cast.rs | 474 |
1 files changed, 136 insertions, 338 deletions
diff --git a/src/eval/cast.rs b/src/eval/cast.rs index 77521f7f..840ceb05 100644 --- a/src/eval/cast.rs +++ b/src/eval/cast.rs @@ -1,18 +1,12 @@ +pub use typst_macros::{cast_from_value, cast_to_value}; + use std::num::NonZeroUsize; use std::ops::Add; -use std::str::FromStr; use ecow::EcoString; -use super::{castable, Array, Dict, Func, Regex, Str, Value}; +use super::{Array, Str, Value}; use crate::diag::StrResult; -use crate::doc::{Destination, Lang, Location, Region}; -use crate::font::{FontStretch, FontStyle, FontWeight}; -use crate::geom::{ - Axes, Color, Corners, Dir, GenAlign, Get, Length, Paint, PartialStroke, Point, Ratio, - Rel, Sides, Smart, -}; -use crate::model::{Content, Label, Selector, Transform}; use crate::syntax::Spanned; /// Cast from a value to a specific type. @@ -32,88 +26,6 @@ pub trait Cast<V = Value>: Sized { } } -/// Describes a possible value for a cast. -#[derive(Debug, Clone, Hash)] -pub enum CastInfo { - /// Any value is okay. - Any, - /// A specific value, plus short documentation for that value. - Value(Value, &'static str), - /// Any value of a type. - Type(&'static str), - /// Multiple alternatives. - Union(Vec<Self>), -} - -impl CastInfo { - /// Produce an error message describing what was expected and what was - /// found. - pub fn error(&self, found: &Value) -> EcoString { - fn accumulate( - info: &CastInfo, - found: &Value, - parts: &mut Vec<EcoString>, - matching_type: &mut bool, - ) { - match info { - CastInfo::Any => parts.push("anything".into()), - CastInfo::Value(value, _) => { - parts.push(value.repr().into()); - if value.type_name() == found.type_name() { - *matching_type = true; - } - } - CastInfo::Type(ty) => parts.push((*ty).into()), - CastInfo::Union(options) => { - for option in options { - accumulate(option, found, parts, matching_type); - } - } - } - } - - let mut matching_type = false; - let mut parts = vec![]; - accumulate(self, found, &mut parts, &mut matching_type); - - let mut msg = String::from("expected "); - if parts.is_empty() { - msg.push_str(" nothing"); - } - - crate::diag::comma_list(&mut msg, &parts, "or"); - - if !matching_type { - msg.push_str(", found "); - msg.push_str(found.type_name()); - } - - msg.into() - } -} - -impl Add for CastInfo { - type Output = Self; - - fn add(self, rhs: Self) -> Self { - Self::Union(match (self, rhs) { - (Self::Union(mut lhs), Self::Union(rhs)) => { - lhs.extend(rhs); - lhs - } - (Self::Union(mut lhs), rhs) => { - lhs.push(rhs); - lhs - } - (lhs, Self::Union(mut rhs)) => { - rhs.insert(0, lhs); - rhs - } - (lhs, rhs) => vec![lhs, rhs], - }) - } -} - impl Cast for Value { fn is(_: &Value) -> bool { true @@ -157,43 +69,15 @@ impl<T: Cast> Cast<Spanned<Value>> for Spanned<T> { } } -castable! { - Dir: "direction", -} - -castable! { - GenAlign: "alignment", -} - -castable! { - Regex: "regular expression", +cast_to_value! { + v: u8 => Value::Int(v as i64) } -castable! { - Selector: "selector", - text: EcoString => Self::text(&text), - label: Label => Self::Label(label), - func: Func => func.select(None)?, - regex: Regex => Self::Regex(regex), +cast_to_value! { + v: u16 => Value::Int(v as i64) } -castable! { - Axes<GenAlign>: "2d alignment", -} - -castable! { - PartialStroke: "stroke", - thickness: Length => Self { - paint: Smart::Auto, - thickness: Smart::Custom(thickness), - }, - color: Color => Self { - paint: Smart::Custom(color.into()), - thickness: Smart::Auto, - }, -} - -castable! { +cast_from_value! { u32, int: i64 => int.try_into().map_err(|_| { if int < 0 { @@ -204,7 +88,15 @@ castable! { })?, } -castable! { +cast_to_value! { + v: u32 => Value::Int(v as i64) +} + +cast_to_value! { + v: i32 => Value::Int(v as i64) +} + +cast_from_value! { usize, int: i64 => int.try_into().map_err(|_| { if int < 0 { @@ -215,7 +107,11 @@ castable! { })?, } -castable! { +cast_to_value! { + v: usize => Value::Int(v as i64) +} + +cast_from_value! { NonZeroUsize, int: i64 => int .try_into() @@ -227,12 +123,11 @@ castable! { })?, } -castable! { - Paint, - color: Color => Self::Solid(color), +cast_to_value! { + v: NonZeroUsize => Value::Int(v.get() as i64) } -castable! { +cast_from_value! { char, string: Str => { let mut chars = string.chars(); @@ -243,131 +138,30 @@ castable! { }, } -castable! { - EcoString, - string: Str => string.into(), -} - -castable! { - String, - string: Str => string.into(), -} - -castable! { - Transform, - content: Content => Self::Content(content), - func: Func => { - if func.argc().map_or(false, |count| count != 1) { - Err("function must have exactly one parameter")? - } - Self::Func(func) - }, -} - -castable! { - Axes<Option<GenAlign>>, - align: GenAlign => { - let mut aligns = Axes::default(); - aligns.set(align.axis(), Some(align)); - aligns - }, - aligns: Axes<GenAlign> => aligns.map(Some), -} - -castable! { - Axes<Rel<Length>>, - array: Array => { - let mut iter = array.into_iter(); - match (iter.next(), iter.next(), iter.next()) { - (Some(a), Some(b), None) => Axes::new(a.cast()?, b.cast()?), - _ => Err("point array must contain exactly two entries")?, - } - }, -} - -castable! { - Location, - mut dict: Dict => { - let page = dict.take("page")?.cast()?; - let x: Length = dict.take("x")?.cast()?; - let y: Length = dict.take("y")?.cast()?; - dict.finish(&["page", "x", "y"])?; - Self { page, pos: Point::new(x.abs, y.abs) } - }, -} - -castable! { - Destination, - loc: Location => Self::Internal(loc), - string: EcoString => Self::Url(string), +cast_to_value! { + v: char => Value::Str(v.into()) } -castable! { - FontStyle, - /// The default, typically upright style. - "normal" => Self::Normal, - /// A cursive style with custom letterform. - "italic" => Self::Italic, - /// Just a slanted version of the normal style. - "oblique" => Self::Oblique, +cast_to_value! { + v: &str => Value::Str(v.into()) } -castable! { - FontWeight, - v: i64 => Self::from_number(v.clamp(0, u16::MAX as i64) as u16), - /// Thin weight (100). - "thin" => Self::THIN, - /// Extra light weight (200). - "extralight" => Self::EXTRALIGHT, - /// Light weight (300). - "light" => Self::LIGHT, - /// Regular weight (400). - "regular" => Self::REGULAR, - /// Medium weight (500). - "medium" => Self::MEDIUM, - /// Semibold weight (600). - "semibold" => Self::SEMIBOLD, - /// Bold weight (700). - "bold" => Self::BOLD, - /// Extrabold weight (800). - "extrabold" => Self::EXTRABOLD, - /// Black weight (900). - "black" => Self::BLACK, +cast_from_value! { + EcoString, + v: Str => v.into(), } -castable! { - FontStretch, - v: Ratio => Self::from_ratio(v.get() as f32), +cast_to_value! { + v: EcoString => Value::Str(v.into()) } -castable! { - Lang, - string: EcoString => Self::from_str(&string)?, +cast_from_value! { + String, + v: Str => v.into(), } -castable! { - Region, - string: EcoString => Self::from_str(&string)?, -} - -/// Castable from [`Value::None`]. -pub struct NoneValue; - -impl Cast for NoneValue { - fn is(value: &Value) -> bool { - matches!(value, Value::None) - } - - fn cast(value: Value) -> StrResult<Self> { - match value { - Value::None => Ok(Self), - _ => <Self as Cast>::error(value), - } - } - - fn describe() -> CastInfo { - CastInfo::Type("none") - } +cast_to_value! { + v: String => Value::Str(v.into()) } impl<T: Cast> Cast for Option<T> { @@ -388,126 +182,130 @@ impl<T: Cast> Cast for Option<T> { } } -/// Castable from [`Value::Auto`]. -pub struct AutoValue; +impl<T: Into<Value>> From<Option<T>> for Value { + fn from(v: Option<T>) -> Self { + match v { + Some(v) => v.into(), + None => Value::None, + } + } +} -impl Cast for AutoValue { +impl<T: Cast> Cast for Vec<T> { fn is(value: &Value) -> bool { - matches!(value, Value::Auto) + Array::is(value) } fn cast(value: Value) -> StrResult<Self> { - match value { - Value::Auto => Ok(Self), - _ => <Self as Cast>::error(value), - } + value.cast::<Array>()?.into_iter().map(Value::cast).collect() } fn describe() -> CastInfo { - CastInfo::Type("auto") + <Array as Cast>::describe() } } -impl<T: Cast> Cast for Smart<T> { - fn is(value: &Value) -> bool { - matches!(value, Value::Auto) || T::is(value) +impl<T: Into<Value>> From<Vec<T>> for Value { + fn from(v: Vec<T>) -> Self { + Value::Array(v.into_iter().map(Into::into).collect()) } +} - fn cast(value: Value) -> StrResult<Self> { - match value { - Value::Auto => Ok(Self::Auto), - v if T::is(&v) => Ok(Self::Custom(T::cast(v)?)), - _ => <Self as Cast>::error(value), +/// Describes a possible value for a cast. +#[derive(Debug, Clone, Hash)] +pub enum CastInfo { + /// Any value is okay. + Any, + /// A specific value, plus short documentation for that value. + Value(Value, &'static str), + /// Any value of a type. + Type(&'static str), + /// Multiple alternatives. + Union(Vec<Self>), +} + +impl CastInfo { + /// Produce an error message describing what was expected and what was + /// found. + pub fn error(&self, found: &Value) -> EcoString { + fn accumulate( + info: &CastInfo, + found: &Value, + parts: &mut Vec<EcoString>, + matching_type: &mut bool, + ) { + match info { + CastInfo::Any => parts.push("anything".into()), + CastInfo::Value(value, _) => { + parts.push(value.repr().into()); + if value.type_name() == found.type_name() { + *matching_type = true; + } + } + CastInfo::Type(ty) => parts.push((*ty).into()), + CastInfo::Union(options) => { + for option in options { + accumulate(option, found, parts, matching_type); + } + } + } } - } - fn describe() -> CastInfo { - T::describe() + CastInfo::Type("auto") - } -} + let mut matching_type = false; + let mut parts = vec![]; + accumulate(self, found, &mut parts, &mut matching_type); -impl<T> Cast for Sides<Option<T>> -where - T: Cast + Copy, -{ - fn is(value: &Value) -> bool { - matches!(value, Value::Dict(_)) || T::is(value) - } + let mut msg = String::from("expected "); + if parts.is_empty() { + msg.push_str(" nothing"); + } - fn cast(mut value: Value) -> StrResult<Self> { - if let Value::Dict(dict) = &mut value { - let mut take = |key| dict.take(key).ok().map(T::cast).transpose(); - - let rest = take("rest")?; - let x = take("x")?.or(rest); - let y = take("y")?.or(rest); - let sides = Sides { - left: take("left")?.or(x), - top: take("top")?.or(y), - right: take("right")?.or(x), - bottom: take("bottom")?.or(y), - }; - - dict.finish(&["left", "top", "right", "bottom", "x", "y", "rest"])?; - - Ok(sides) - } else if T::is(&value) { - Ok(Self::splat(Some(T::cast(value)?))) - } else { - <Self as Cast>::error(value) + crate::diag::comma_list(&mut msg, &parts, "or"); + + if !matching_type { + msg.push_str(", found "); + msg.push_str(found.type_name()); } + + msg.into() } +} - fn describe() -> CastInfo { - T::describe() + CastInfo::Type("dictionary") +impl Add for CastInfo { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + Self::Union(match (self, rhs) { + (Self::Union(mut lhs), Self::Union(rhs)) => { + lhs.extend(rhs); + lhs + } + (Self::Union(mut lhs), rhs) => { + lhs.push(rhs); + lhs + } + (lhs, Self::Union(mut rhs)) => { + rhs.insert(0, lhs); + rhs + } + (lhs, rhs) => vec![lhs, rhs], + }) } } -impl<T> Cast for Corners<Option<T>> -where - T: Cast + Copy, -{ - fn is(value: &Value) -> bool { - matches!(value, Value::Dict(_)) || T::is(value) +/// Castable from nothing. +pub enum Never {} + +impl Cast for Never { + fn is(_: &Value) -> bool { + false } - fn cast(mut value: Value) -> StrResult<Self> { - if let Value::Dict(dict) = &mut value { - let mut take = |key| dict.take(key).ok().map(T::cast).transpose(); - - let rest = take("rest")?; - let left = take("left")?.or(rest); - let top = take("top")?.or(rest); - let right = take("right")?.or(rest); - let bottom = take("bottom")?.or(rest); - let corners = Corners { - top_left: take("top-left")?.or(top).or(left), - top_right: take("top-right")?.or(top).or(right), - bottom_right: take("bottom-right")?.or(bottom).or(right), - bottom_left: take("bottom-left")?.or(bottom).or(left), - }; - - dict.finish(&[ - "top-left", - "top-right", - "bottom-right", - "bottom-left", - "left", - "top", - "right", - "bottom", - "rest", - ])?; - - Ok(corners) - } else if T::is(&value) { - Ok(Self::splat(Some(T::cast(value)?))) - } else { - <Self as Cast>::error(value) - } + fn cast(value: Value) -> StrResult<Self> { + <Self as Cast>::error(value) } fn describe() -> CastInfo { - T::describe() + CastInfo::Type("dictionary") + CastInfo::Union(vec![]) } } |
