diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-06-06 21:13:59 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-06-06 22:06:16 +0200 |
| commit | fd417da04f7ca4b995de7f6510abafd3e9c31307 (patch) | |
| tree | 3675529c75ca7363701ac8ea306de2cc1d3cbcb3 /src/model | |
| parent | 168bdf35bd773e67343c965cb473492cc5cae9e7 (diff) | |
Improve value casting infrastructure
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 32 | ||||
| -rw-r--r-- | src/model/element.rs | 31 | ||||
| -rw-r--r-- | src/model/introspect.rs | 8 | ||||
| -rw-r--r-- | src/model/label.rs | 16 | ||||
| -rw-r--r-- | src/model/mod.rs | 20 | ||||
| -rw-r--r-- | src/model/selector.rs | 291 | ||||
| -rw-r--r-- | src/model/styles.rs | 313 |
7 files changed, 368 insertions, 343 deletions
diff --git a/src/model/content.rs b/src/model/content.rs index f262d027..015f8b76 100644 --- a/src/model/content.rs +++ b/src/model/content.rs @@ -8,11 +8,11 @@ use ecow::{eco_format, EcoString, EcoVec}; use super::{ element, Behave, Behaviour, ElemFunc, Element, Fold, Guard, Label, Locatable, - Location, PlainText, Recipe, Selector, Style, Styles, Synthesize, + Location, Recipe, Selector, Style, Styles, Synthesize, }; use crate::diag::{SourceResult, StrResult}; use crate::doc::Meta; -use crate::eval::{Cast, Dict, Str, Value, Vm}; +use crate::eval::{Dict, FromValue, IntoValue, Str, Value, Vm}; use crate::syntax::Span; use crate::util::pretty_array_like; @@ -153,23 +153,24 @@ impl Content { pub fn with_field( mut self, name: impl Into<EcoString>, - value: impl Into<Value>, + value: impl IntoValue, ) -> Self { self.push_field(name, value); self } /// Attach a field to the content. - pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl Into<Value>) { + pub fn push_field(&mut self, name: impl Into<EcoString>, value: impl IntoValue) { let name = name.into(); if let Some(i) = self.attrs.iter().position(|attr| match attr { Attr::Field(field) => *field == name, _ => false, }) { - self.attrs.make_mut()[i + 1] = Attr::Value(Prehashed::new(value.into())); + self.attrs.make_mut()[i + 1] = + Attr::Value(Prehashed::new(value.into_value())); } else { self.attrs.push(Attr::Field(name)); - self.attrs.push(Attr::Value(Prehashed::new(value.into()))); + self.attrs.push(Attr::Value(Prehashed::new(value.into_value()))); } } @@ -226,7 +227,7 @@ impl Content { } /// Try to access a field on the content as a specified type. - pub fn cast_field<T: Cast>(&self, name: &str) -> Option<T> { + pub fn cast_field<T: FromValue>(&self, name: &str) -> Option<T> { match self.field(name) { Some(value) => value.cast().ok(), None => None, @@ -235,7 +236,7 @@ impl Content { /// Expect a field on the content to exist as a specified type. #[track_caller] - pub fn expect_field<T: Cast>(&self, name: &str) -> T { + pub fn expect_field<T: FromValue>(&self, name: &str) -> T { self.field(name).unwrap().cast().unwrap() } @@ -311,12 +312,9 @@ impl Content { } } - /// Repeat this content `n` times. - pub fn repeat(&self, n: i64) -> StrResult<Self> { - let count = usize::try_from(n) - .map_err(|_| format!("cannot repeat this content {} times", n))?; - - Ok(Self::sequence(vec![self.clone(); count])) + /// Repeat this content `count` times. + pub fn repeat(&self, count: usize) -> Self { + Self::sequence(vec![self.clone(); count]) } /// Disable a show rule recipe. @@ -599,6 +597,12 @@ impl Fold for Vec<Meta> { } } +/// Tries to extract the plain-text representation of the element. +pub trait PlainText { + /// Write this element's plain text into the given buffer. + fn plain_text(&self, text: &mut EcoString); +} + /// The missing key access error message when no default value was given. #[cold] fn missing_field_no_default(key: &str) -> EcoString { diff --git a/src/model/element.rs b/src/model/element.rs index e26848b1..c673ee41 100644 --- a/src/model/element.rs +++ b/src/model/element.rs @@ -2,14 +2,11 @@ use std::any::TypeId; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; -use ecow::EcoString; use once_cell::sync::Lazy; use super::{Content, Selector, Styles}; use crate::diag::SourceResult; -use crate::eval::{ - cast_from_value, cast_to_value, Args, Dict, Func, FuncInfo, Value, Vm, -}; +use crate::eval::{cast, Args, Dict, Func, FuncInfo, Value, Vm}; /// A document element. pub trait Element: Construct + Set + Sized + 'static { @@ -110,15 +107,12 @@ impl Hash for ElemFunc { } } -cast_from_value! { +cast! { ElemFunc, + self => Value::Func(self.into()), v: Func => v.element().ok_or("expected element function")?, } -cast_to_value! { - v: ElemFunc => Value::Func(v.into()) -} - impl From<&'static NativeElemFunc> for ElemFunc { fn from(native: &'static NativeElemFunc) -> Self { Self(native) @@ -138,22 +132,3 @@ pub struct NativeElemFunc { /// Details about the function. pub info: Lazy<FuncInfo>, } - -/// A label for an element. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct Label(pub EcoString); - -impl Debug for Label { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "<{}>", self.0) - } -} - -/// Indicates that an element cannot be labelled. -pub trait Unlabellable {} - -/// Tries to extract the plain-text representation of the element. -pub trait PlainText { - /// Write this element's plain text into the given buffer. - fn plain_text(&self, text: &mut EcoString); -} diff --git a/src/model/introspect.rs b/src/model/introspect.rs index 87a17a8e..f00f89f5 100644 --- a/src/model/introspect.rs +++ b/src/model/introspect.rs @@ -11,12 +11,12 @@ use indexmap::IndexMap; use super::{Content, Selector}; use crate::diag::StrResult; use crate::doc::{Frame, FrameItem, Meta, Position}; -use crate::eval::{cast_from_value, Value}; +use crate::eval::{cast, Value}; use crate::geom::{Point, Transform}; use crate::model::Label; use crate::util::NonZeroExt; -/// Uniquely identifies an element in the document across multiple layout passes. +/// Identifies the location of an element in the document. /// /// This struct is created by [`Locator::locate`]. #[derive(Copy, Clone, Eq, PartialEq, Hash)] @@ -46,8 +46,8 @@ impl Debug for Location { } } -cast_from_value! { - Location: "location", +cast! { + type Location: "location", } /// Provides locations for elements in the document. diff --git a/src/model/label.rs b/src/model/label.rs new file mode 100644 index 00000000..ef8f3edd --- /dev/null +++ b/src/model/label.rs @@ -0,0 +1,16 @@ +use std::fmt::{self, Debug, Formatter}; + +use ecow::EcoString; + +/// A label for an element. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct Label(pub EcoString); + +impl Debug for Label { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "<{}>", self.0) + } +} + +/// Indicates that an element cannot be labelled. +pub trait Unlabellable {} diff --git a/src/model/mod.rs b/src/model/mod.rs index e541cd01..632b691f 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -3,16 +3,26 @@ mod content; mod element; mod introspect; +mod label; mod realize; +mod selector; mod styles; +#[doc(inline)] pub use typst_macros::element; -pub use self::content::*; -pub use self::element::*; -pub use self::introspect::*; -pub use self::realize::*; -pub use self::styles::*; +pub use self::content::{Content, MetaElem, PlainText}; +pub use self::element::{Construct, ElemFunc, Element, NativeElemFunc, Set}; +pub use self::introspect::{Introspector, Location, Locator}; +pub use self::label::{Label, Unlabellable}; +pub use self::realize::{ + applicable, realize, Behave, Behaviour, Finalize, Guard, Locatable, Show, Synthesize, +}; +pub use self::selector::{LocatableSelector, Selector, ShowableSelector}; +pub use self::styles::{ + Fold, Property, Recipe, Resolve, Style, StyleChain, StyleVec, StyleVecBuilder, + Styles, Transform, +}; use std::mem::ManuallyDrop; diff --git a/src/model/selector.rs b/src/model/selector.rs new file mode 100644 index 00000000..c1528c0a --- /dev/null +++ b/src/model/selector.rs @@ -0,0 +1,291 @@ +use std::any::{Any, TypeId}; +use std::fmt::{self, Debug, Formatter, Write}; +use std::sync::Arc; + +use ecow::{eco_format, EcoString, EcoVec}; + +use super::{Content, ElemFunc, Label, Location}; +use crate::diag::StrResult; +use crate::eval::{ + cast, CastInfo, Dict, FromValue, Func, IntoValue, Reflect, Regex, Value, +}; +use crate::model::Locatable; +use crate::util::pretty_array_like; + +/// A selector in a show rule. +#[derive(Clone, PartialEq, Hash)] +pub enum Selector { + /// Matches a specific type of element. + /// + /// If there is a dictionary, only elements with the fields from the + /// dictionary match. + Elem(ElemFunc, Option<Dict>), + /// Matches the element at the specified location. + Location(Location), + /// Matches elements with a specific label. + Label(Label), + /// Matches text elements through a regular expression. + Regex(Regex), + /// Matches elements with a specific capability. + Can(TypeId), + /// Matches if any of the subselectors match. + Or(EcoVec<Self>), + /// Matches if all of the subselectors match. + And(EcoVec<Self>), + /// Matches all matches of `selector` before `end`. + Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool }, + /// Matches all matches of `selector` after `start`. + After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool }, +} + +impl Selector { + /// Define a simple text selector. + pub fn text(text: &str) -> Self { + Self::Regex(Regex::new(®ex::escape(text)).unwrap()) + } + + /// Define a simple [`Selector::Can`] selector. + pub fn can<T: ?Sized + Any>() -> Self { + Self::Can(TypeId::of::<T>()) + } + + /// Transforms this selector and an iterator of other selectors into a + /// [`Selector::Or`] selector. + pub fn and(self, others: impl IntoIterator<Item = Self>) -> Self { + Self::And(others.into_iter().chain(Some(self)).collect()) + } + + /// Transforms this selector and an iterator of other selectors into a + /// [`Selector::And`] selector. + pub fn or(self, others: impl IntoIterator<Item = Self>) -> Self { + Self::Or(others.into_iter().chain(Some(self)).collect()) + } + + /// Transforms this selector into a [`Selector::Before`] selector. + pub fn before(self, location: impl Into<Self>, inclusive: bool) -> Self { + Self::Before { + selector: Arc::new(self), + end: Arc::new(location.into()), + inclusive, + } + } + + /// Transforms this selector into a [`Selector::After`] selector. + pub fn after(self, location: impl Into<Self>, inclusive: bool) -> Self { + Self::After { + selector: Arc::new(self), + start: Arc::new(location.into()), + inclusive, + } + } + + /// Whether the selector matches for the target. + pub fn matches(&self, target: &Content) -> bool { + match self { + Self::Elem(element, dict) => { + target.func() == *element + && dict + .iter() + .flat_map(|dict| dict.iter()) + .all(|(name, value)| target.field_ref(name) == Some(value)) + } + Self::Label(label) => target.label() == Some(label), + Self::Regex(regex) => { + target.func() == item!(text_func) + && item!(text_str)(target).map_or(false, |text| regex.is_match(&text)) + } + Self::Can(cap) => target.can_type_id(*cap), + Self::Or(selectors) => selectors.iter().any(move |sel| sel.matches(target)), + Self::And(selectors) => selectors.iter().all(move |sel| sel.matches(target)), + Self::Location(location) => target.location() == Some(*location), + // Not supported here. + Self::Before { .. } | Self::After { .. } => false, + } + } +} + +impl From<Location> for Selector { + fn from(value: Location) -> Self { + Self::Location(value) + } +} + +impl Debug for Selector { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Elem(elem, dict) => { + f.write_str(elem.name())?; + if let Some(dict) = dict { + f.write_str(".where")?; + dict.fmt(f)?; + } + Ok(()) + } + Self::Label(label) => label.fmt(f), + Self::Regex(regex) => regex.fmt(f), + Self::Can(cap) => cap.fmt(f), + Self::Or(selectors) | Self::And(selectors) => { + f.write_str(if matches!(self, Self::Or(_)) { "or" } else { "and" })?; + let pieces: Vec<_> = + selectors.iter().map(|sel| eco_format!("{sel:?}")).collect(); + f.write_str(&pretty_array_like(&pieces, false)) + } + Self::Location(loc) => loc.fmt(f), + Self::Before { selector, end: split, inclusive } + | Self::After { selector, start: split, inclusive } => { + selector.fmt(f)?; + + if matches!(self, Self::Before { .. }) { + f.write_str(".before(")?; + } else { + f.write_str(".after(")?; + } + + split.fmt(f)?; + if !*inclusive { + f.write_str(", inclusive: false")?; + } + f.write_char(')') + } + } + } +} + +cast! { + type Selector: "selector", + func: Func => func + .element() + .ok_or("only element functions can be used as selectors")? + .select(), + label: Label => Self::Label(label), + text: EcoString => Self::text(&text), + regex: Regex => Self::Regex(regex), + location: Location => Self::Location(location), +} + +/// A selector that can be used with `query`. +/// +/// Hopefully, this is made obsolete by a more powerful query mechanism in the +/// future. +#[derive(Clone, PartialEq, Hash)] +pub struct LocatableSelector(pub Selector); + +impl Reflect for LocatableSelector { + fn describe() -> CastInfo { + CastInfo::Union(vec![ + CastInfo::Type("function"), + CastInfo::Type("label"), + CastInfo::Type("selector"), + ]) + } + + fn castable(value: &Value) -> bool { + matches!(value.type_name(), "function" | "label" | "selector") + } +} + +impl IntoValue for LocatableSelector { + fn into_value(self) -> Value { + self.0.into_value() + } +} + +impl FromValue for LocatableSelector { + fn from_value(value: Value) -> StrResult<Self> { + fn validate(selector: &Selector) -> StrResult<()> { + match selector { + Selector::Elem(elem, _) => { + if !elem.can::<dyn Locatable>() { + Err(eco_format!("{} is not locatable", elem.name()))? + } + } + Selector::Location(_) => {} + Selector::Label(_) => {} + Selector::Regex(_) => Err("text is not locatable")?, + Selector::Can(_) => Err("capability is not locatable")?, + Selector::Or(list) | Selector::And(list) => { + for selector in list { + validate(selector)?; + } + } + Selector::Before { selector, end: split, .. } + | Selector::After { selector, start: split, .. } => { + for selector in [selector, split] { + validate(selector)?; + } + } + } + Ok(()) + } + + if !Self::castable(&value) { + return Err(Self::error(&value)); + } + + let selector = Selector::from_value(value)?; + validate(&selector)?; + Ok(Self(selector)) + } +} + +/// A selector that can be used with show rules. +/// +/// Hopefully, this is made obsolete by a more powerful showing mechanism in the +/// future. +#[derive(Clone, PartialEq, Hash)] +pub struct ShowableSelector(pub Selector); + +impl Reflect for ShowableSelector { + fn describe() -> CastInfo { + CastInfo::Union(vec![ + CastInfo::Type("function"), + CastInfo::Type("label"), + CastInfo::Type("string"), + CastInfo::Type("regex"), + CastInfo::Type("symbol"), + CastInfo::Type("selector"), + ]) + } + + fn castable(value: &Value) -> bool { + matches!( + value.type_name(), + "symbol" | "string" | "label" | "function" | "regex" | "selector" + ) + } +} + +impl IntoValue for ShowableSelector { + fn into_value(self) -> Value { + self.0.into_value() + } +} + +impl FromValue for ShowableSelector { + fn from_value(value: Value) -> StrResult<Self> { + fn validate(selector: &Selector) -> StrResult<()> { + match selector { + Selector::Elem(_, _) => {} + Selector::Label(_) => {} + Selector::Regex(_) => {} + Selector::Or(_) + | Selector::And(_) + | Selector::Location(_) + | Selector::Can(_) + | Selector::Before { .. } + | Selector::After { .. } => { + Err("this selector cannot be used with show")? + } + } + Ok(()) + } + + if !Self::castable(&value) { + return Err(Self::error(&value)); + } + + let selector = Selector::from_value(value)?; + validate(&selector)?; + Ok(Self(selector)) + } +} diff --git a/src/model/styles.rs b/src/model/styles.rs index e30a8a92..5b6430c2 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -1,19 +1,15 @@ -use std::any::{Any, TypeId}; use std::fmt::{self, Debug, Formatter, Write}; use std::iter; use std::mem; use std::ptr; -use std::sync::Arc; use comemo::Prehashed; -use ecow::{eco_format, eco_vec, EcoString, EcoVec}; +use ecow::{eco_vec, EcoString, EcoVec}; -use super::{Content, ElemFunc, Element, Label, Location, Vt}; -use crate::diag::{SourceResult, StrResult, Trace, Tracepoint}; -use crate::eval::{cast_from_value, Args, Cast, CastInfo, Dict, Func, Regex, Value, Vm}; -use crate::model::Locatable; +use super::{Content, ElemFunc, Element, Selector, Vt}; +use crate::diag::{SourceResult, Trace, Tracepoint}; +use crate::eval::{cast, Args, FromValue, Func, IntoValue, Value, Vm}; use crate::syntax::Span; -use crate::util::pretty_array_like; /// A list of style properties. #[derive(Default, PartialEq, Clone, Hash)] @@ -158,8 +154,17 @@ pub struct Property { impl Property { /// Create a new property from a key-value pair. - pub fn new(element: ElemFunc, name: EcoString, value: Value) -> Self { - Self { element, name, value, span: None } + pub fn new( + element: ElemFunc, + name: impl Into<EcoString>, + value: impl IntoValue, + ) -> Self { + Self { + element, + name: name.into(), + value: value.into_value(), + span: None, + } } /// Whether this property is the given one. @@ -254,282 +259,6 @@ impl Debug for Recipe { } } -/// A selector in a show rule. -#[derive(Clone, PartialEq, Hash)] -pub enum Selector { - /// Matches a specific type of element. - /// - /// If there is a dictionary, only elements with the fields from the - /// dictionary match. - Elem(ElemFunc, Option<Dict>), - /// Matches the element at the specified location. - Location(Location), - /// Matches elements with a specific label. - Label(Label), - /// Matches text elements through a regular expression. - Regex(Regex), - /// Matches elements with a specific capability. - Can(TypeId), - /// Matches if any of the subselectors match. - Or(EcoVec<Self>), - /// Matches if all of the subselectors match. - And(EcoVec<Self>), - /// Matches all matches of `selector` before `end`. - Before { selector: Arc<Self>, end: Arc<Self>, inclusive: bool }, - /// Matches all matches of `selector` after `start`. - After { selector: Arc<Self>, start: Arc<Self>, inclusive: bool }, -} - -impl Selector { - /// Define a simple text selector. - pub fn text(text: &str) -> Self { - Self::Regex(Regex::new(®ex::escape(text)).unwrap()) - } - - /// Define a simple [`Selector::Can`] selector. - pub fn can<T: ?Sized + Any>() -> Self { - Self::Can(TypeId::of::<T>()) - } - - /// Transforms this selector and an iterator of other selectors into a - /// [`Selector::Or`] selector. - pub fn and(self, others: impl IntoIterator<Item = Self>) -> Self { - Self::And(others.into_iter().chain(Some(self)).collect()) - } - - /// Transforms this selector and an iterator of other selectors into a - /// [`Selector::And`] selector. - pub fn or(self, others: impl IntoIterator<Item = Self>) -> Self { - Self::Or(others.into_iter().chain(Some(self)).collect()) - } - - /// Transforms this selector into a [`Selector::Before`] selector. - pub fn before(self, location: impl Into<Self>, inclusive: bool) -> Self { - Self::Before { - selector: Arc::new(self), - end: Arc::new(location.into()), - inclusive, - } - } - - /// Transforms this selector into a [`Selector::After`] selector. - pub fn after(self, location: impl Into<Self>, inclusive: bool) -> Self { - Self::After { - selector: Arc::new(self), - start: Arc::new(location.into()), - inclusive, - } - } - - /// Whether the selector matches for the target. - pub fn matches(&self, target: &Content) -> bool { - match self { - Self::Elem(element, dict) => { - target.func() == *element - && dict - .iter() - .flat_map(|dict| dict.iter()) - .all(|(name, value)| target.field_ref(name) == Some(value)) - } - Self::Label(label) => target.label() == Some(label), - Self::Regex(regex) => { - target.func() == item!(text_func) - && item!(text_str)(target).map_or(false, |text| regex.is_match(&text)) - } - Self::Can(cap) => target.can_type_id(*cap), - Self::Or(selectors) => selectors.iter().any(move |sel| sel.matches(target)), - Self::And(selectors) => selectors.iter().all(move |sel| sel.matches(target)), - Self::Location(location) => target.location() == Some(*location), - // Not supported here. - Self::Before { .. } | Self::After { .. } => false, - } - } -} - -impl From<Location> for Selector { - fn from(value: Location) -> Self { - Self::Location(value) - } -} - -impl Debug for Selector { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Self::Elem(elem, dict) => { - f.write_str(elem.name())?; - if let Some(dict) = dict { - f.write_str(".where")?; - dict.fmt(f)?; - } - Ok(()) - } - Self::Label(label) => label.fmt(f), - Self::Regex(regex) => regex.fmt(f), - Self::Can(cap) => cap.fmt(f), - Self::Or(selectors) | Self::And(selectors) => { - f.write_str(if matches!(self, Self::Or(_)) { "or" } else { "and" })?; - let pieces: Vec<_> = - selectors.iter().map(|sel| eco_format!("{sel:?}")).collect(); - f.write_str(&pretty_array_like(&pieces, false)) - } - Self::Location(loc) => loc.fmt(f), - Self::Before { selector, end: split, inclusive } - | Self::After { selector, start: split, inclusive } => { - selector.fmt(f)?; - - if matches!(self, Self::Before { .. }) { - f.write_str(".before(")?; - } else { - f.write_str(".after(")?; - } - - split.fmt(f)?; - if !*inclusive { - f.write_str(", inclusive: false")?; - } - f.write_char(')') - } - } - } -} - -cast_from_value! { - Selector: "selector", - func: Func => func - .element() - .ok_or("only element functions can be used as selectors")? - .select(), - label: Label => Self::Label(label), - text: EcoString => Self::text(&text), - regex: Regex => Self::Regex(regex), - location: Location => Self::Location(location), -} - -/// A selector that can be used with `query`. -/// -/// Hopefully, this is made obsolete by a more powerful query mechanism in the -/// future. -#[derive(Clone, PartialEq, Hash)] -pub struct LocatableSelector(pub Selector); - -impl Cast for LocatableSelector { - fn is(value: &Value) -> bool { - matches!(value, Value::Label(_) | Value::Func(_)) - || value.type_name() == "selector" - } - - fn cast(value: Value) -> StrResult<Self> { - fn validate(selector: &Selector) -> StrResult<()> { - match selector { - Selector::Elem(elem, _) => { - if !elem.can::<dyn Locatable>() { - Err(eco_format!("{} is not locatable", elem.name()))? - } - } - Selector::Location(_) => {} - Selector::Label(_) => {} - Selector::Regex(_) => Err("text is not locatable")?, - Selector::Can(_) => Err("capability is not locatable")?, - Selector::Or(list) | Selector::And(list) => { - for selector in list { - validate(selector)?; - } - } - Selector::Before { selector, end: split, .. } - | Selector::After { selector, start: split, .. } => { - for selector in [selector, split] { - validate(selector)?; - } - } - } - Ok(()) - } - - if !Self::is(&value) { - return <Self as Cast>::error(value); - } - - let selector = Selector::cast(value)?; - validate(&selector)?; - Ok(Self(selector)) - } - - fn describe() -> CastInfo { - CastInfo::Union(vec![ - CastInfo::Type("label"), - CastInfo::Type("function"), - CastInfo::Type("selector"), - ]) - } -} - -impl From<LocatableSelector> for Value { - fn from(value: LocatableSelector) -> Self { - value.0.into() - } -} - -/// A selector that can be used with show rules. -/// -/// Hopefully, this is made obsolete by a more powerful showing mechanism in the -/// future. -#[derive(Clone, PartialEq, Hash)] -pub struct ShowableSelector(pub Selector); - -impl Cast for ShowableSelector { - fn is(value: &Value) -> bool { - matches!( - value, - Value::Symbol(_) | Value::Str(_) | Value::Label(_) | Value::Func(_) - ) || value.type_name() == "regex" - || value.type_name() == "selector" - } - - fn cast(value: Value) -> StrResult<Self> { - fn validate(selector: &Selector) -> StrResult<()> { - match selector { - Selector::Elem(_, _) => {} - Selector::Label(_) => {} - Selector::Regex(_) => {} - Selector::Or(_) - | Selector::And(_) - | Selector::Location(_) - | Selector::Can(_) - | Selector::Before { .. } - | Selector::After { .. } => { - Err("this selector cannot be used with show")? - } - } - Ok(()) - } - - if !Self::is(&value) { - return <Self as Cast>::error(value); - } - - let selector = Selector::cast(value)?; - validate(&selector)?; - Ok(Self(selector)) - } - - fn describe() -> CastInfo { - CastInfo::Union(vec![ - CastInfo::Type("function"), - CastInfo::Type("label"), - CastInfo::Type("string"), - CastInfo::Type("regex"), - CastInfo::Type("symbol"), - CastInfo::Type("selector"), - ]) - } -} - -impl From<ShowableSelector> for Value { - fn from(value: ShowableSelector) -> Self { - value.0.into() - } -} - /// A show rule transformation that can be applied to a match. #[derive(Clone, PartialEq, Hash)] pub enum Transform { @@ -551,7 +280,7 @@ impl Debug for Transform { } } -cast_from_value! { +cast! { Transform, content: Content => Self::Content(content), func: Func => Self::Func(func), @@ -592,7 +321,7 @@ impl<'a> StyleChain<'a> { } /// Cast the first value for the given property in the chain. - pub fn get<T: Cast>( + pub fn get<T: FromValue>( self, func: ElemFunc, name: &'a str, @@ -605,7 +334,7 @@ impl<'a> StyleChain<'a> { } /// Cast the first value for the given property in the chain. - pub fn get_resolve<T: Cast + Resolve>( + pub fn get_resolve<T: FromValue + Resolve>( self, func: ElemFunc, name: &'a str, @@ -616,7 +345,7 @@ impl<'a> StyleChain<'a> { } /// Cast the first value for the given property in the chain. - pub fn get_fold<T: Cast + Fold>( + pub fn get_fold<T: FromValue + Fold>( self, func: ElemFunc, name: &'a str, @@ -645,7 +374,7 @@ impl<'a> StyleChain<'a> { default: impl Fn() -> <T::Output as Fold>::Output, ) -> <T::Output as Fold>::Output where - T: Cast + Resolve, + T: FromValue + Resolve, T::Output: Fold, { fn next<T>( @@ -671,7 +400,7 @@ impl<'a> StyleChain<'a> { } /// Iterate over all values for the given property in the chain. - pub fn properties<T: Cast + 'a>( + pub fn properties<T: FromValue + 'a>( self, func: ElemFunc, name: &'a str, |
