summaryrefslogtreecommitdiff
path: root/src/model/styles.rs
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/styles.rs
parent168bdf35bd773e67343c965cb473492cc5cae9e7 (diff)
Improve value casting infrastructure
Diffstat (limited to 'src/model/styles.rs')
-rw-r--r--src/model/styles.rs313
1 files changed, 21 insertions, 292 deletions
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,