diff options
Diffstat (limited to 'library/src/meta')
| -rw-r--r-- | library/src/meta/bibliography.rs | 9 | ||||
| -rw-r--r-- | library/src/meta/context.rs | 28 | ||||
| -rw-r--r-- | library/src/meta/counter.rs | 48 | ||||
| -rw-r--r-- | library/src/meta/document.rs | 7 | ||||
| -rw-r--r-- | library/src/meta/figure.rs | 19 | ||||
| -rw-r--r-- | library/src/meta/footnote.rs | 2 | ||||
| -rw-r--r-- | library/src/meta/heading.rs | 6 | ||||
| -rw-r--r-- | library/src/meta/link.rs | 13 | ||||
| -rw-r--r-- | library/src/meta/mod.rs | 16 | ||||
| -rw-r--r-- | library/src/meta/numbering.rs | 56 | ||||
| -rw-r--r-- | library/src/meta/outline.rs | 30 | ||||
| -rw-r--r-- | library/src/meta/query.rs | 19 | ||||
| -rw-r--r-- | library/src/meta/reference.rs | 19 | ||||
| -rw-r--r-- | library/src/meta/state.rs | 22 |
14 files changed, 119 insertions, 175 deletions
diff --git a/library/src/meta/bibliography.rs b/library/src/meta/bibliography.rs index a2491bb5..5e5145b1 100644 --- a/library/src/meta/bibliography.rs +++ b/library/src/meta/bibliography.rs @@ -84,16 +84,13 @@ pub struct BibliographyElem { #[derive(Debug, Default, Clone, Hash)] pub struct BibPaths(Vec<EcoString>); -cast_from_value! { +cast! { BibPaths, + self => self.0.into_value(), v: EcoString => Self(vec![v]), v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?), } -cast_to_value! { - v: BibPaths => v.0.into() -} - impl BibliographyElem { /// Find the document's bibliography. pub fn find(introspector: Tracked<Introspector>) -> StrResult<Self> { @@ -374,7 +371,7 @@ impl Show for CiteElem { } } -cast_from_value! { +cast! { CiteElem, v: Content => v.to::<Self>().cloned().ok_or("expected citation")?, } diff --git a/library/src/meta/context.rs b/library/src/meta/context.rs index ad466305..d599c63e 100644 --- a/library/src/meta/context.rs +++ b/library/src/meta/context.rs @@ -46,7 +46,6 @@ use crate::prelude::*; /// /// Display: Locate /// Category: meta -/// Returns: content #[func] pub fn locate( /// A function that receives a `location`. Its return value is displayed @@ -56,8 +55,8 @@ pub fn locate( /// `locate` appears in the document. That makes it possible to generate /// content that depends on its own location in the document. func: Func, -) -> Value { - LocateElem::new(func).pack().into() +) -> Content { + LocateElem::new(func).pack() } /// Executes a `locate` call. @@ -79,7 +78,7 @@ impl Show for LocateElem { } let location = self.0.location().unwrap(); - Ok(self.func().call_vt(vt, [location.into()])?.display()) + Ok(self.func().call_vt(vt, [location])?.display()) } } @@ -102,7 +101,6 @@ impl Show for LocateElem { /// /// Display: Style /// Category: meta -/// Returns: content #[func] pub fn style( /// A function to call with the styles. Its return value is displayed @@ -112,8 +110,8 @@ pub fn style( /// `style` appears in the document. That makes it possible to generate /// content that depends on the style context it appears in. func: Func, -) -> Value { - StyleElem::new(func).pack().into() +) -> Content { + StyleElem::new(func).pack() } /// Executes a style access. @@ -130,7 +128,7 @@ struct StyleElem { impl Show for StyleElem { #[tracing::instrument(name = "StyleElem::show", skip_all)] fn show(&self, vt: &mut Vt, styles: StyleChain) -> SourceResult<Content> { - Ok(self.func().call_vt(vt, [styles.to_map().into()])?.display()) + Ok(self.func().call_vt(vt, [styles.to_map()])?.display()) } } @@ -177,7 +175,6 @@ impl Show for StyleElem { /// /// Display: Layout /// Category: meta -/// Returns: content #[func] pub fn layout( /// A function to call with the outer container's size. Its return value is @@ -190,8 +187,8 @@ pub fn layout( /// `layout` appears in the document. That makes it possible to generate /// content that depends on the size of the container it is inside of. func: Func, -) -> Value { - LayoutElem::new(func).pack().into() +) -> Content { + LayoutElem::new(func).pack() } /// Executes a `layout` call. @@ -213,16 +210,13 @@ impl Layout for LayoutElem { styles: StyleChain, regions: Regions, ) -> SourceResult<Fragment> { - // Gets the current region's base size, which will be the size of the outer container, - // or of the page if there is no such container. + // Gets the current region's base size, which will be the size of the + // outer container, or of the page if there is no such container. let Size { x, y } = regions.base(); - let size_dict = dict! { "width" => x, "height" => y }.into(); - let result = self .func() - .call_vt(vt, [size_dict])? // calls func(size) + .call_vt(vt, [dict! { "width" => x, "height" => y }])? .display(); - result.layout(vt, styles, regions) } } diff --git a/library/src/meta/counter.rs b/library/src/meta/counter.rs index 46992d38..ef4646ab 100644 --- a/library/src/meta/counter.rs +++ b/library/src/meta/counter.rs @@ -277,7 +277,6 @@ use crate::prelude::*; /// /// Display: Counter /// Category: meta -/// Returns: counter #[func] pub fn counter( /// The key that identifies this counter. @@ -288,8 +287,8 @@ pub fn counter( /// - If this is an element function or selector, counts through its elements, /// - If this is the [`page`]($func/page) function, counts through pages. key: CounterKey, -) -> Value { - Value::dynamic(Counter::new(key)) +) -> Counter { + Counter::new(key) } /// Counts through pages, elements, and more. @@ -317,17 +316,17 @@ impl Counter { span: Span, ) -> SourceResult<Value> { let value = match method { - "display" => { - self.display(args.eat()?, args.named("both")?.unwrap_or(false)).into() - } + "display" => self + .display(args.eat()?, args.named("both")?.unwrap_or(false)) + .into_value(), "step" => self .update(CounterUpdate::Step( args.named("level")?.unwrap_or(NonZeroUsize::ONE), )) - .into(), - "update" => self.update(args.expect("value or function")?).into(), - "at" => self.at(&mut vm.vt, args.expect("location")?)?.into(), - "final" => self.final_(&mut vm.vt, args.expect("location")?)?.into(), + .into_value(), + "update" => self.update(args.expect("value or function")?).into_value(), + "at" => self.at(&mut vm.vt, args.expect("location")?)?.into_value(), + "final" => self.final_(&mut vm.vt, args.expect("location")?)?.into_value(), _ => bail!(span, "type counter has no method `{}`", method), }; args.finish()?; @@ -342,10 +341,7 @@ impl Counter { /// Get the value of the state at the given location. pub fn at(&self, vt: &mut Vt, location: Location) -> SourceResult<CounterState> { let sequence = self.sequence(vt)?; - let offset = vt - .introspector - .query(&Selector::before(self.selector(), location, true)) - .len(); + let offset = vt.introspector.query(&self.selector().before(location, true)).len(); let (mut state, page) = sequence[offset].clone(); if self.is_page() { let delta = vt.introspector.page(location).get().saturating_sub(page.get()); @@ -479,8 +475,8 @@ impl Debug for Counter { } } -cast_from_value! { - Counter: "counter", +cast! { + type Counter: "counter", } /// Identifies a counter. @@ -495,7 +491,7 @@ pub enum CounterKey { Str(Str), } -cast_from_value! { +cast! { CounterKey, v: Str => Self::Str(v), label: Label => Self::Selector(Selector::Label(label)), @@ -503,7 +499,7 @@ cast_from_value! { if v == PageElem::func() { Self::Page } else { - Self::Selector(LocatableSelector::cast(Value::from(v))?.0) + Self::Selector(LocatableSelector::from_value(v.into_value())?.0) } }, selector: LocatableSelector => Self::Selector(selector.0), @@ -536,8 +532,8 @@ impl Debug for CounterUpdate { } } -cast_from_value! { - CounterUpdate: "counter update", +cast! { + type CounterUpdate: "counter update", v: CounterState => Self::Set(v), v: Func => Self::Func(v), } @@ -559,10 +555,7 @@ impl CounterState { CounterUpdate::Set(state) => *self = state, CounterUpdate::Step(level) => self.step(level, 1), CounterUpdate::Func(func) => { - *self = func - .call_vt(vt, self.0.iter().copied().map(Into::into))? - .cast() - .at(func.span())? + *self = func.call_vt(vt, self.0.iter().copied())?.cast().at(func.span())? } } Ok(()) @@ -593,8 +586,9 @@ impl CounterState { } } -cast_from_value! { +cast! { CounterState, + self => Value::Array(self.0.into_iter().map(IntoValue::into_value).collect()), num: usize => Self(smallvec![num]), array: Array => Self(array .into_iter() @@ -602,10 +596,6 @@ cast_from_value! { .collect::<StrResult<_>>()?), } -cast_to_value! { - v: CounterState => Value::Array(v.0.into_iter().map(Into::into).collect()) -} - /// Executes a display of a state. /// /// Display: State diff --git a/library/src/meta/document.rs b/library/src/meta/document.rs index 1ce900ed..db036e0a 100644 --- a/library/src/meta/document.rs +++ b/library/src/meta/document.rs @@ -78,12 +78,9 @@ impl LayoutRoot for DocumentElem { #[derive(Debug, Default, Clone, Hash)] pub struct Author(Vec<EcoString>); -cast_from_value! { +cast! { Author, + self => self.0.into_value(), v: EcoString => Self(vec![v]), v: Array => Self(v.into_iter().map(Value::cast).collect::<StrResult<_>>()?), } - -cast_to_value! { - v: Author => v.0.into() -} diff --git a/library/src/meta/figure.rs b/library/src/meta/figure.rs index 67087832..0d218770 100644 --- a/library/src/meta/figure.rs +++ b/library/src/meta/figure.rs @@ -209,7 +209,7 @@ impl Synthesize for FigureElem { }; let target = descendant.unwrap_or_else(|| self.body()); - supplement.resolve(vt, [target.into()])? + supplement.resolve(vt, [target])? } }; @@ -312,8 +312,8 @@ impl FigureElem { self.counter(), self.numbering(StyleChain::default()), ) { - let numbers = - counter.at(vt, self.0.location().unwrap())?.display(vt, &numbering)?; + let loc = self.0.location().unwrap(); + let numbers = counter.at(vt, loc)?.display(vt, &numbering)?; if !supplement.is_empty() { supplement += TextElem::packed("\u{a0}"); @@ -335,19 +335,16 @@ pub enum FigureKind { Name(EcoString), } -cast_from_value! { +cast! { FigureKind, + self => match self { + Self::Elem(v) => v.into_value(), + Self::Name(v) => v.into_value(), + }, v: ElemFunc => Self::Elem(v), v: EcoString => Self::Name(v), } -cast_to_value! { - v: FigureKind => match v { - FigureKind::Elem(v) => v.into(), - FigureKind::Name(v) => v.into(), - } -} - /// An element that can be auto-detected in a figure. /// /// This trait is used to determine the type of a figure. diff --git a/library/src/meta/footnote.rs b/library/src/meta/footnote.rs index 22de91c3..1c95716c 100644 --- a/library/src/meta/footnote.rs +++ b/library/src/meta/footnote.rs @@ -211,7 +211,7 @@ impl Finalize for FootnoteEntry { } } -cast_from_value! { +cast! { FootnoteElem, v: Content => v.to::<Self>().cloned().unwrap_or_else(|| Self::new(v.clone())), } diff --git a/library/src/meta/heading.rs b/library/src/meta/heading.rs index d6ad7044..7a333e36 100644 --- a/library/src/meta/heading.rs +++ b/library/src/meta/heading.rs @@ -104,9 +104,7 @@ impl Synthesize for HeadingElem { let supplement = match self.supplement(styles) { Smart::Auto => TextElem::packed(self.local_name_in(styles)), Smart::Custom(None) => Content::empty(), - Smart::Custom(Some(supplement)) => { - supplement.resolve(vt, [self.clone().into()])? - } + Smart::Custom(Some(supplement)) => supplement.resolve(vt, [self.clone()])?, }; self.push_level(self.level(styles)); @@ -164,7 +162,7 @@ impl Count for HeadingElem { } } -cast_from_value! { +cast! { HeadingElem, v: Content => v.to::<Self>().ok_or("expected heading")?.clone(), } diff --git a/library/src/meta/link.rs b/library/src/meta/link.rs index 93b9999d..43f6a34d 100644 --- a/library/src/meta/link.rs +++ b/library/src/meta/link.rs @@ -125,19 +125,16 @@ pub enum LinkTarget { Label(Label), } -cast_from_value! { +cast! { LinkTarget, + self => match self { + Self::Dest(v) => v.into_value(), + Self::Label(v) => v.into_value(), + }, v: Destination => Self::Dest(v), v: Label => Self::Label(v), } -cast_to_value! { - v: LinkTarget => match v { - LinkTarget::Dest(v) => v.into(), - LinkTarget::Label(v) => v.into(), - } -} - impl From<Destination> for LinkTarget { fn from(dest: Destination) -> Self { Self::Dest(dest) diff --git a/library/src/meta/mod.rs b/library/src/meta/mod.rs index 724e5d20..dcac6379 100644 --- a/library/src/meta/mod.rs +++ b/library/src/meta/mod.rs @@ -42,14 +42,14 @@ pub(super) fn define(global: &mut Scope) { global.define("footnote", FootnoteElem::func()); global.define("cite", CiteElem::func()); global.define("bibliography", BibliographyElem::func()); - global.define("locate", locate); - global.define("style", style); - global.define("layout", layout); - global.define("counter", counter); - global.define("numbering", numbering); - global.define("state", state); - global.define("query", query); - global.define("selector", selector); + global.define("locate", locate_func()); + global.define("style", style_func()); + global.define("layout", layout_func()); + global.define("counter", counter_func()); + global.define("numbering", numbering_func()); + global.define("state", state_func()); + global.define("query", query_func()); + global.define("selector", selector_func()); } /// The named with which an element is referenced. diff --git a/library/src/meta/numbering.rs b/library/src/meta/numbering.rs index fbe7306c..ef0d0f70 100644 --- a/library/src/meta/numbering.rs +++ b/library/src/meta/numbering.rs @@ -32,14 +32,13 @@ use crate::text::Case; /// /// Display: Numbering /// Category: meta -/// Returns: any #[func] pub fn numbering( /// Defines how the numbering works. /// - /// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`, - /// `א`, `가`, `ㄱ`, and `*`. They are replaced by the number in the sequence, - /// in the given case. + /// **Counting symbols** are `1`, `a`, `A`, `i`, `I`, `い`, `イ`, `א`, `가`, + /// `ㄱ`, and `*`. They are replaced by the number in the sequence, in the + /// given case. /// /// The `*` character means that symbols should be used to count, in the /// order of `*`, `†`, `‡`, `§`, `¶`, and `‖`. If there are more than six @@ -52,9 +51,9 @@ pub fn numbering( /// suffixes. They are repeated as-is at in front of their rendered /// equivalent of their counting symbol. /// - /// This parameter can also be an arbitrary function that gets each number as - /// an individual argument. When given a function, the `numbering` function - /// just forwards the arguments to that function. While this is not + /// This parameter can also be an arbitrary function that gets each number + /// as an individual argument. When given a function, the `numbering` + /// function just forwards the arguments to that function. While this is not /// particularly useful in itself, it means that you can just give arbitrary /// numberings to the `numbering` function without caring whether they are /// defined as a pattern or function. @@ -65,8 +64,10 @@ pub fn numbering( /// given, the last counting symbol with its prefix is repeated. #[variadic] numbers: Vec<usize>, -) -> Value { - numbering.apply_vm(vm, &numbers)? + /// The virtual machine. + vm: &mut Vm, +) -> SourceResult<Value> { + numbering.apply_vm(vm, &numbers) } /// How to number a sequence of things. @@ -84,8 +85,7 @@ impl Numbering { Ok(match self { Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), Self::Func(func) => { - let args = - Args::new(func.span(), numbers.iter().map(|&n| Value::Int(n as i64))); + let args = Args::new(func.span(), numbers.iter().copied()); func.call_vm(vm, args)? } }) @@ -95,9 +95,7 @@ impl Numbering { pub fn apply_vt(&self, vt: &mut Vt, numbers: &[usize]) -> SourceResult<Value> { Ok(match self { Self::Pattern(pattern) => Value::Str(pattern.apply(numbers).into()), - Self::Func(func) => { - func.call_vt(vt, numbers.iter().map(|&n| Value::Int(n as i64)))? - } + Self::Func(func) => func.call_vt(vt, numbers.iter().copied())?, }) } @@ -116,19 +114,16 @@ impl From<NumberingPattern> for Numbering { } } -cast_from_value! { +cast! { Numbering, + self => match self { + Self::Pattern(pattern) => pattern.into_value(), + Self::Func(func) => func.into_value(), + }, v: NumberingPattern => Self::Pattern(v), v: Func => Self::Func(v), } -cast_to_value! { - v: Numbering => match v { - Numbering::Pattern(pattern) => pattern.into(), - Numbering::Func(func) => func.into(), - } -} - /// How to turn a number into text. /// /// A pattern consists of a prefix, followed by one of `1`, `a`, `A`, `i`, @@ -230,15 +225,11 @@ impl FromStr for NumberingPattern { } } -cast_from_value! { +cast! { NumberingPattern, - v: Str => v.parse()?, -} - -cast_to_value! { - v: NumberingPattern => { + self => { let mut pat = EcoString::new(); - for (prefix, kind, case) in &v.pieces { + for (prefix, kind, case) in &self.pieces { pat.push_str(prefix); let mut c = kind.to_char(); if *case == Case::Upper { @@ -246,9 +237,10 @@ cast_to_value! { } pat.push(c); } - pat.push_str(&v.suffix); - pat.into() - } + pat.push_str(&self.suffix); + pat.into_value() + }, + v: Str => v.parse()?, } /// Different kinds of numberings. diff --git a/library/src/meta/outline.rs b/library/src/meta/outline.rs index 089eceb2..894c3b8b 100644 --- a/library/src/meta/outline.rs +++ b/library/src/meta/outline.rs @@ -367,19 +367,14 @@ impl OutlineIndent { // Length => indent with some fixed spacing per level Some(Smart::Custom(OutlineIndent::Length(length))) => { - let Ok(depth): Result<i64, _> = ancestors.len().try_into() else { - bail!(span, "outline element depth too large"); - }; - - let hspace = HElem::new(*length).pack().repeat(depth).unwrap(); - seq.push(hspace); + seq.push(HElem::new(*length).pack().repeat(ancestors.len())); } // Function => call function with the current depth and take // the returned content Some(Smart::Custom(OutlineIndent::Function(func))) => { let depth = ancestors.len(); - let returned = func.call_vt(vt, [depth.into()])?; + let returned = func.call_vt(vt, [depth])?; let Ok(returned) = returned.cast::<Content>() else { bail!( span, @@ -396,17 +391,14 @@ impl OutlineIndent { } } -cast_from_value! { +cast! { OutlineIndent, - b: bool => OutlineIndent::Bool(b), - s: Spacing => OutlineIndent::Length(s), - f: Func => OutlineIndent::Function(f), -} - -cast_to_value! { - v: OutlineIndent => match v { - OutlineIndent::Bool(b) => b.into(), - OutlineIndent::Length(s) => s.into(), - OutlineIndent::Function(f) => f.into() - } + self => match self { + Self::Bool(v) => v.into_value(), + Self::Length(v) => v.into_value(), + Self::Function(v) => v.into_value() + }, + v: bool => OutlineIndent::Bool(v), + v: Spacing => OutlineIndent::Length(v), + v: Func => OutlineIndent::Function(v), } diff --git a/library/src/meta/query.rs b/library/src/meta/query.rs index ad7274f1..7f839f97 100644 --- a/library/src/meta/query.rs +++ b/library/src/meta/query.rs @@ -98,7 +98,6 @@ use crate::prelude::*; /// /// Display: Query /// Category: meta -/// Returns: array #[func] pub fn query( /// Can be an element function like a `heading` or `figure`, a `{<label>}` @@ -111,7 +110,6 @@ pub fn query( /// have an explicit label attached to them. This limitation will be /// resolved in the future. target: LocatableSelector, - /// Can be any location. Why is it required then? As noted before, Typst has /// to evaluate parts of your code multiple times to determine the values of /// all state. By only allowing this function within @@ -120,14 +118,14 @@ pub fn query( /// level of a module, the evaluation of the whole module and its exports /// could depend on the query's result. location: Location, -) -> Value { + /// The virtual machine. + vm: &mut Vm, +) -> Array { let _ = location; let vec = vm.vt.introspector.query(&target.0); - Value::Array( - vec.into_iter() - .map(|elem| Value::Content(elem.into_inner())) - .collect(), - ) + vec.into_iter() + .map(|elem| Value::Content(elem.into_inner())) + .collect() } /// Turns a value into a selector. The following values are accepted: @@ -137,12 +135,11 @@ pub fn query( /// /// Display: Selector /// Category: meta -/// Returns: content #[func] pub fn selector( /// Can be an element function like a `heading` or `figure`, a `{<label>}` /// or a more complex selector like `{heading.where(level: 1)}`. target: Selector, -) -> Value { - target.into() +) -> Selector { + target } diff --git a/library/src/meta/reference.rs b/library/src/meta/reference.rs index c538b696..42450b97 100644 --- a/library/src/meta/reference.rs +++ b/library/src/meta/reference.rs @@ -193,7 +193,7 @@ impl Show for RefElem { Smart::Auto => refable.supplement(), Smart::Custom(None) => Content::empty(), Smart::Custom(Some(supplement)) => { - supplement.resolve(vt, [(*elem).clone().into()])? + supplement.resolve(vt, [(*elem).clone()])? } }; @@ -229,10 +229,10 @@ pub enum Supplement { impl Supplement { /// Tries to resolve the supplement into its content. - pub fn resolve( + pub fn resolve<T: IntoValue>( &self, vt: &mut Vt, - args: impl IntoIterator<Item = Value>, + args: impl IntoIterator<Item = T>, ) -> SourceResult<Content> { Ok(match self { Supplement::Content(content) => content.clone(), @@ -241,19 +241,16 @@ impl Supplement { } } -cast_from_value! { +cast! { Supplement, + self => match self { + Self::Content(v) => v.into_value(), + Self::Func(v) => v.into_value(), + }, v: Content => Self::Content(v), v: Func => Self::Func(v), } -cast_to_value! { - v: Supplement => match v { - Supplement::Content(v) => v.into(), - Supplement::Func(v) => v.into(), - } -} - /// Marks an element as being able to be referenced. This is used to implement /// the `@ref` element. pub trait Refable { diff --git a/library/src/meta/state.rs b/library/src/meta/state.rs index 284daf54..231852e3 100644 --- a/library/src/meta/state.rs +++ b/library/src/meta/state.rs @@ -233,7 +233,6 @@ use crate::prelude::*; /// /// Display: State /// Category: meta -/// Returns: state #[func] pub fn state( /// The key that identifies this state. @@ -241,8 +240,8 @@ pub fn state( /// The initial value of the state. #[default] init: Value, -) -> Value { - Value::dynamic(State { key, init }) +) -> State { + State { key, init } } /// A state. @@ -265,10 +264,10 @@ impl State { span: Span, ) -> SourceResult<Value> { let value = match method { - "display" => self.display(args.eat()?).into(), + "display" => self.display(args.eat()?).into_value(), "at" => self.at(&mut vm.vt, args.expect("location")?)?, "final" => self.final_(&mut vm.vt, args.expect("location")?)?, - "update" => self.update(args.expect("value or function")?).into(), + "update" => self.update(args.expect("value or function")?).into_value(), _ => bail!(span, "type state has no method `{}`", method), }; args.finish()?; @@ -284,10 +283,7 @@ impl State { #[tracing::instrument(skip(self, vt))] pub fn at(self, vt: &mut Vt, location: Location) -> SourceResult<Value> { let sequence = self.sequence(vt)?; - let offset = vt - .introspector - .query(&Selector::before(self.selector(), location, true)) - .len(); + let offset = vt.introspector.query(&self.selector().before(location, true)).len(); Ok(sequence[offset].clone()) } @@ -358,8 +354,8 @@ impl Debug for State { } } -cast_from_value! { - State: "state", +cast! { + type State: "state", } /// An update to perform on a state. @@ -377,8 +373,8 @@ impl Debug for StateUpdate { } } -cast_from_value! { - StateUpdate: "state update", +cast! { + type StateUpdate: "state update", v: Func => Self::Func(v), v: Value => Self::Set(v), } |
