summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-06-06 21:13:59 +0200
committerLaurenz <laurmaedje@gmail.com>2023-06-06 22:06:16 +0200
commitfd417da04f7ca4b995de7f6510abafd3e9c31307 (patch)
tree3675529c75ca7363701ac8ea306de2cc1d3cbcb3 /src/model
parent168bdf35bd773e67343c965cb473492cc5cae9e7 (diff)
Improve value casting infrastructure
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs32
-rw-r--r--src/model/element.rs31
-rw-r--r--src/model/introspect.rs8
-rw-r--r--src/model/label.rs16
-rw-r--r--src/model/mod.rs20
-rw-r--r--src/model/selector.rs291
-rw-r--r--src/model/styles.rs313
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(&regex::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(&regex::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,