From 5e09f5a4bb386ab9cb3a80f4e74ac521a817a4e8 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 9 May 2025 18:17:10 +0300 Subject: [PATCH] wip: dsl, input, output, proc: more precise lifetimes --- dsl/src/dsl_context.rs | 43 ++++++++++++------------- dsl/src/dsl_token.rs | 26 ++++++++-------- input/src/input_dsl.rs | 58 +++++++++++++++++----------------- output/src/ops/align.rs | 2 +- output/src/ops/bsp.rs | 2 +- output/src/ops/cond.rs | 8 ++--- output/src/ops/transform.rs | 28 +++++++++-------- output/src/view.rs | 62 +++++++++++++++++++++++-------------- proc/src/proc_command.rs | 11 ++++--- proc/src/proc_expose.rs | 16 +++++----- proc/src/proc_view.rs | 6 ++-- 11 files changed, 140 insertions(+), 122 deletions(-) diff --git a/dsl/src/dsl_context.rs b/dsl/src/dsl_context.rs index b6daf5f..4acbf63 100644 --- a/dsl/src/dsl_context.rs +++ b/dsl/src/dsl_context.rs @@ -1,11 +1,17 @@ use crate::*; -pub trait TryFromDsl<'a, T>: Sized { - fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option { +pub trait TryFromDsl<'state, T>: Sized { + fn try_from_expr <'source: 'state> ( + _state: &'state T, _iter: TokenIter<'source> + ) -> Option { None } - fn try_from_atom (state: &'a T, value: Value<'a>) -> Option { - if let Exp(0, iter) = value { return Self::try_from_expr(state, iter) } + fn try_from_atom <'source: 'state> ( + state: &'state T, value: Value<'source> + ) -> Option { + if let Exp(0, iter) = value { + return Self::try_from_expr(state, iter.clone()) + } None } } @@ -15,29 +21,20 @@ pub trait TryIntoDsl: Sized { } /// Map EDN tokens to parameters of a given type for a given context -pub trait Context: Sized { - fn get (&self, _atom: &Value) -> Option { +pub trait Context<'state, U>: Sized { + fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option { None } - fn get_or_fail (&self, dsl: &Value) -> U { - self.get(dsl).expect("no value") +} + +impl<'state, T: Context<'state, U>, U> Context<'state, U> for &T { + fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option { + (*self).get(iter) } } -impl, U> Context for &T { - fn get (&self, dsl: &Value) -> Option { - (*self).get(dsl) - } - fn get_or_fail (&self, dsl: &Value) -> U { - (*self).get_or_fail(dsl) - } -} - -impl, U> Context for Option { - fn get (&self, dsl: &Value) -> Option { - self.as_ref().map(|s|s.get(dsl)).flatten() - } - fn get_or_fail (&self, dsl: &Value) -> U { - self.as_ref().map(|s|s.get_or_fail(dsl)).expect("no provider") +impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option { + fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option { + self.as_ref().map(|s|s.get(iter)).flatten() } } diff --git a/dsl/src/dsl_token.rs b/dsl/src/dsl_token.rs index d58a60e..e26ee93 100644 --- a/dsl/src/dsl_token.rs +++ b/dsl/src/dsl_token.rs @@ -22,38 +22,38 @@ //!``` use crate::*; -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> { - pub source: &'a str, +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> { + pub source: &'source str, pub start: usize, pub length: usize, - pub value: Value<'a>, + pub value: Value<'source>, } -#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'a> { +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum Value<'source> { #[default] Nil, Err(ParseError), Num(usize), - Sym(&'a str), - Key(&'a str), - Str(&'a str), - Exp(usize, TokenIter<'a>), + Sym(&'source str), + Key(&'source str), + Str(&'source str), + Exp(usize, TokenIter<'source>), } -impl<'a> Token<'a> { - pub const fn new (source: &'a str, start: usize, length: usize, value: Value<'a>) -> Self { +impl<'source> Token<'source> { + pub const fn new (source: &'source str, start: usize, length: usize, value: Value<'source>) -> Self { Self { source, start, length, value } } pub const fn end (&self) -> usize { self.start.saturating_add(self.length) } - pub const fn slice (&'a self) -> &'a str { + pub const fn slice (&'source self) -> &'source str { self.slice_source(self.source) //str_range(self.source, self.start, self.end()) } - pub const fn slice_source <'b> (&'a self, source: &'b str) -> &'b str { + pub const fn slice_source <'range> (&'source self, source: &'range str) -> &'range str { str_range(source, self.start, self.end()) } - pub const fn slice_source_exp <'b> (&'a self, source: &'b str) -> &'b str { + pub const fn slice_source_exp <'range> (&'source self, source: &'range str) -> &'range str { str_range(source, self.start.saturating_add(1), self.end()) } pub const fn value (&self) -> Value { diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 49f77fc..bb5346d 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -2,9 +2,9 @@ use crate::*; use std::marker::PhantomData; /// A [Command] that can be constructed from a [Token]. -pub trait DslCommand<'a, C>: TryFromDsl<'a, C> + Command {} +pub trait DslCommand<'state, C>: TryFromDsl<'state, C> + Command {} -impl<'a, C, T: TryFromDsl<'a, C> + Command> DslCommand<'a, C> for T {} +impl<'state, C, T: TryFromDsl<'state, C> + Command> DslCommand<'state, C> for T {} /// [Input] state that can be matched against a [Value]. pub trait DslInput: Input { @@ -12,14 +12,14 @@ pub trait DslInput: Input { } /// A pre-configured mapping of input events to commands. -pub trait KeyMap<'a, S, C: DslCommand<'a, S>, I: DslInput> { +pub trait KeyMap<'state, S, C: DslCommand<'state, S>, I: DslInput> { /// Try to find a command that matches the current input event. - fn command (&'a self, state: &'a S, input: &'a I) -> Option; + fn command (&'state self, state: &'state S, input: &'state I) -> Option; } /// A [SourceIter] can be a [KeyMap]. -impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIter<'a> { - fn command (&'a self, state: &'a S, input: &'a I) -> Option { +impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for SourceIter<'state> { + fn command (&'state self, state: &'state S, input: &'state I) -> Option { let mut iter = self.clone(); while let Some((token, rest)) = iter.next() { iter = rest; @@ -45,8 +45,8 @@ impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIte } /// A [TokenIter] can be a [KeyMap]. -impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter<'a> { - fn command (&'a self, state: &'a S, input: &'a I) -> Option { +impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for TokenIter<'state> { + fn command (&'state self, state: &'state S, input: &'state I) -> Option { let mut iter = self.clone(); while let Some(next) = iter.next() { match next { @@ -70,25 +70,25 @@ impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter } } -pub type InputLayerCond<'a, S> = Boxbool + Send + Sync + 'a>; +pub type InputLayerCond<'state, S> = Boxbool + Send + Sync + 'state>; /// A collection of pre-configured mappings of input events to commands, /// which may be made available subject to given conditions. -pub struct InputMap<'a, S, C, I, M> +pub struct InputMap<'state, S, C, I, M> where - C: DslCommand<'a, S>, + C: DslCommand<'state, S>, I: DslInput, - M: KeyMap<'a, S, C, I> + Send + Sync + M: KeyMap<'state, S, C, I> + Send + Sync { - __: &'a PhantomData<(S, C, I)>, - pub layers: Vec<(InputLayerCond<'a, S>, M)>, + __: &'state PhantomData<(S, C, I)>, + pub layers: Vec<(InputLayerCond<'state, S>, M)>, } -impl<'a, S, C, I, M> Default for InputMap<'a, S, C, I, M> +impl<'state, S, C, I, M> Default for InputMap<'state, S, C, I, M> where - C: DslCommand<'a, S>, + C: DslCommand<'state, S>, I: DslInput, - M: KeyMap<'a, S, C, I> + Send + Sync + M: KeyMap<'state, S, C, I> + Send + Sync { fn default () -> Self { Self { @@ -98,11 +98,11 @@ where } } -impl<'a, S, C, I, M> InputMap<'a, S, C, I, M> +impl<'state, S, C, I, M> InputMap<'state, S, C, I, M> where - C: DslCommand<'a, S>, + C: DslCommand<'state, S>, I: DslInput, - M: KeyMap<'a, S, C, I> + Send + Sync + M: KeyMap<'state, S, C, I> + Send + Sync { pub fn new (keymap: M) -> Self { Self::default().layer(keymap) @@ -115,21 +115,21 @@ where self.add_layer_if(Box::new(|_|true), keymap); self } - pub fn layer_if (mut self, condition: InputLayerCond<'a, S>, keymap: M) -> Self { + pub fn layer_if (mut self, condition: InputLayerCond<'state, S>, keymap: M) -> Self { self.add_layer_if(condition, keymap); self } - pub fn add_layer_if (&mut self, condition: InputLayerCond<'a, S>, keymap: M) -> &mut Self { + pub fn add_layer_if (&mut self, condition: InputLayerCond<'state, S>, keymap: M) -> &mut Self { self.layers.push((Box::new(condition), keymap)); self } } -impl<'a, S, C, I, M> std::fmt::Debug for InputMap<'a, S, C, I, M> +impl<'state, S, C, I, M> std::fmt::Debug for InputMap<'state, S, C, I, M> where - C: DslCommand<'a, S>, + C: DslCommand<'state, S>, I: DslInput, - M: KeyMap<'a, S, C, I> + Send + Sync + M: KeyMap<'state, S, C, I> + Send + Sync { fn fmt (&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { write!(f, "[InputMap: {} layer(s)]", self.layers.len()); @@ -138,13 +138,13 @@ where } /// An [InputMap] can be a [KeyMap]. -impl<'a, S, C, I, M> KeyMap<'a, S, C, I> for InputMap<'a, S, C, I, M> +impl<'state, S, C, I, M> KeyMap<'state, S, C, I> for InputMap<'state, S, C, I, M> where - C: DslCommand<'a, S>, + C: DslCommand<'state, S>, I: DslInput, - M: KeyMap<'a, S, C, I> + Send + Sync + M: KeyMap<'state, S, C, I> + Send + Sync { - fn command (&'a self, state: &'a S, input: &'a I) -> Option { + fn command (&'state self, state: &'state S, input: &'state I) -> Option { for (condition, keymap) in self.layers.iter() { if !condition(state) { continue diff --git a/output/src/ops/align.rs b/output/src/ops/align.rs index 35a2b68..d4c7fb3 100644 --- a/output/src/ops/align.rs +++ b/output/src/ops/align.rs @@ -36,7 +36,7 @@ pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } pub struct Align(Alignment, A); #[cfg(feature = "dsl")] -try_from_expr!(<'a, E>: Align>: |state, iter|{ +try_from_expr!(<'source, 'state, E>: Align>: |state, iter|{ if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { match key { "align/c"|"align/x"|"align/y"| diff --git a/output/src/ops/bsp.rs b/output/src/ops/bsp.rs index 5ede569..0ab80c3 100644 --- a/output/src/ops/bsp.rs +++ b/output/src/ops/bsp.rs @@ -17,7 +17,7 @@ impl, B: Content> Content for Bsp { } } #[cfg(feature = "dsl")] -try_from_expr!(<'a, E>: Bsp, RenderBox<'a, E>>: |state, iter| { +try_from_expr!(<'source, 'state, E>: Bsp, RenderBox<'state, E>>: |state, iter| { if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { match key { "bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b" => { diff --git a/output/src/ops/cond.rs b/output/src/ops/cond.rs index f92f157..521fdf8 100644 --- a/output/src/ops/cond.rs +++ b/output/src/ops/cond.rs @@ -19,12 +19,12 @@ impl Either { } #[cfg(feature = "dsl")] -try_from_expr!(<'a, E>: When>: |state, iter| { +try_from_expr!(<'source, 'state, E>: When>: |state, iter| { if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() { let _ = iter.next().unwrap(); let condition = iter.next().expect("no condition specified"); - let condition = state.get(&condition.value).expect("no condition provided"); + let condition = state.get(&mut iter).expect("no condition provided"); let content = iter.next().expect("no content specified"); let content = if let Some(content) = state.get_content(&content.value) { @@ -38,12 +38,12 @@ try_from_expr!(<'a, E>: When>: |state, iter| { }); #[cfg(feature = "dsl")] -try_from_expr!(<'a, E>: Either, RenderBox<'a, E>>: |state, iter| { +try_from_expr!(<'source, 'state, E>: Either, RenderBox<'state, E>>: |state, iter| { if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() { let _ = iter.next().unwrap(); let condition = iter.next().expect("no condition specified"); - let condition = state.get(&condition.value).expect("no condition provided"); + let condition = state.get(&mut iter).expect("no condition provided"); let content = iter.next().expect("no content specified"); let content = if let Some(content) = state.get_content(&content.value) { diff --git a/output/src/ops/transform.rs b/output/src/ops/transform.rs index 6012a96..0dd2264 100644 --- a/output/src/ops/transform.rs +++ b/output/src/ops/transform.rs @@ -33,9 +33,11 @@ macro_rules! transform_xy { #[inline] pub const fn xy (item: T) -> Self { Self::XY(item) } } #[cfg(feature = "dsl")] - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> - for $Enum> { - fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { + impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T> + for $Enum> { + fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>) + -> Option + { let mut iter = iter.clone(); if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { if k == $x || k == $y || k == $xy { @@ -80,17 +82,16 @@ macro_rules! transform_xy_unit { #[inline] pub const fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } #[cfg(feature = "dsl")] - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> - for $Enum> { - fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { + impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T> + for $Enum> { + fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>) -> Option { let mut iter = iter.clone(); if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { if k == $x || k == $y { let _ = iter.next().unwrap(); - let u = iter.next().expect("no unit specified"); - let u = get_value!(state => u); - let c = iter.next().expect("no content specified"); - let c = get_content!(state => c); + let u = state.get(&mut iter).expect("no unit specified"); + let c = state.get_content(&iter.next().expect("no content specified").value) + .expect("no content provided"); return Some(match k { $x => Self::x(u, c), $y => Self::y(u, c), @@ -98,9 +99,10 @@ macro_rules! transform_xy_unit { }) } else if k == $xy { let _ = iter.next().unwrap(); - let u = get_value!(state => iter.next().expect("no unit specified")); - let v = get_value!(state => iter.next().expect("no unit specified")); - let c = get_content!(state => iter.next().expect("no content specified")); + let u = state.get(&mut iter).expect("no unit specified"); + let v = state.get(&mut iter).expect("no unit specified"); + let c = state.get_content(&iter.next().expect("no content specified").value) + .expect("no content provided"); return Some(Self::xy(u, v, c)) } } diff --git a/output/src/view.rs b/output/src/view.rs index c18713f..4bf3d73 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -45,34 +45,37 @@ impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content for View<'a, T> { // Provides components to the view. #[cfg(feature = "dsl")] -pub trait ViewContext<'a, E: Output + 'a>: Send + Sync - + Context - + Context - + Context +pub trait ViewContext<'state, E: Output + 'state>: Send + Sync + + Context<'state, bool> + + Context<'state, usize> + + Context<'state, E::Unit> { - fn get_content (&'a self, value: &Value<'a>) -> Option> { + fn get_content <'source: 'state> (&'state self, value: &Value<'source>) -> Option> { match value { Value::Sym(_) => self.get_content_sym(value), Value::Exp(_, _) => self.get_content_exp(value), _ => panic!("only :symbols and (expressions) accepted here") } } - fn get_content_sym (&'a self, value: &Value<'a>) -> Option>; - fn get_content_exp (&'a self, value: &Value<'a>) -> Option> { - try_delegate!(self, *value, When::>); - try_delegate!(self, *value, Either::, RenderBox<'a, E>>); - try_delegate!(self, *value, Align::>); - try_delegate!(self, *value, Bsp::, RenderBox<'a, E>>); - try_delegate!(self, *value, Fill::>); - try_delegate!(self, *value, Fixed::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Min::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Max::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Shrink::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Expand::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Push::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Pull::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Margin::<_, RenderBox<'a, E>>); - try_delegate!(self, *value, Padding::<_, RenderBox<'a, E>>); + fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>) + -> Option>; + fn get_content_exp <'source: 'state> (&'state self, value: &Value<'source>) + -> Option> + { + try_delegate!(self, *value, When::>); + try_delegate!(self, *value, Either::, RenderBox<'state, E>>); + try_delegate!(self, *value, Align::>); + try_delegate!(self, *value, Bsp::, RenderBox<'state, E>>); + try_delegate!(self, *value, Fill::>); + try_delegate!(self, *value, Fixed::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Min::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Max::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Shrink::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Expand::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Push::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Pull::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Margin::<_, RenderBox<'state, E>>); + try_delegate!(self, *value, Padding::<_, RenderBox<'state, E>>); None } } @@ -88,9 +91,20 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync #[cfg(feature = "dsl")] #[macro_export] macro_rules! try_from_expr { - (<$l:lifetime, $E:ident>: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => { - impl<$l, $E: Output + $l, T: ViewContext<$l, $E>> TryFromDsl<$l, T> for $Struct { - fn try_from_expr ($state: &$l T, $iter: TokenIter<'a>) -> Option { + (< + $lt_source:lifetime, + $lt_state:lifetime, + $Output:ident + >: $Struct:ty: |$state:ident, $iter:ident|$body:expr) => { + impl< + $lt_state, + $Output: Output + $lt_state, + T: ViewContext<$lt_state, $Output> + > TryFromDsl<$lt_state, T> for $Struct { + fn try_from_expr <$lt_source: $lt_state> ( + $state: &$lt_state T, + $iter: TokenIter<$lt_source> + ) -> Option { let mut $iter = $iter.clone(); $body; None diff --git a/proc/src/proc_command.rs b/proc/src/proc_command.rs index b9ececc..456d797 100644 --- a/proc/src/proc_command.rs +++ b/proc/src/proc_command.rs @@ -68,8 +68,10 @@ impl ToTokens for CommandDef { } #block /// Generated by [tengri_proc]. - impl<'a> TryFromDsl<'a, #target> for #enumeration { - fn try_from_expr (state: &#target, iter: TokenIter) -> Option { + impl<'state> TryFromDsl<'state, #target> for #enumeration { + fn try_from_expr <'source: 'state> ( + state: &'state #target, iter: TokenIter<'source> + ) -> Option { let mut iter = iter.clone(); match iter.next() { #(#matchers)* @@ -148,11 +150,10 @@ impl CommandArm { for (arg, ty) in self.args() { let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})", quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site()); - let give_err = LitStr::new(&format!("{}: missing value \"{}\" ({})", + let give_err = LitStr::new(&format!("{}: missing value for \"{}\" ({})", quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site()); write_quote_to(&mut out, quote! { - #arg : Context::get(state, &iter.next().expect(#take_err).value) - .expect(#give_err) , + #arg: Context::get(state, &mut iter).expect(#give_err), }); } out diff --git a/proc/src/proc_expose.rs b/proc/src/proc_expose.rs index affb192..15e08e8 100644 --- a/proc/src/proc_expose.rs +++ b/proc/src/proc_expose.rs @@ -66,8 +66,8 @@ impl ToTokens for ExposeImpl { let formatted_type = format!("{}", quote! { #t }); let predefined = match formatted_type.as_str() { "bool" => quote! { - ::tengri::dsl::Value::Sym(":true") => true, - ::tengri::dsl::Value::Sym(":false") => false, + Some(::tengri::dsl::Value::Sym(":true")) => true, + Some(::tengri::dsl::Value::Sym(":false")) => false, }, "u8" | "u16" | "u32" | "u64" | "usize" | "i8" | "i16" | "i32" | "i64" | "isize" => { @@ -76,7 +76,7 @@ impl ToTokens for ExposeImpl { Span::call_site() ); quote! { - ::tengri::dsl::Value::Num(n) => TryInto::<#t>::try_into(*n) + Some(::tengri::dsl::Value::Num(n)) => TryInto::<#t>::try_into(n) .unwrap_or_else(|_|panic!(#num_err)), } }, @@ -85,9 +85,11 @@ impl ToTokens for ExposeImpl { let values = variants.iter().map(ExposeArm::from); write_quote_to(out, quote! { /// Generated by [tengri_proc]. - impl ::tengri::dsl::Context<#t> for #target { - fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> { - Some(match dsl { + impl<'state> ::tengri::dsl::Context<'state, #t> for #target { + fn get <'source> ( + &self, iter: &mut ::tengri::dsl::TokenIter<'source> + ) -> Option<#t> { + Some(match iter.next().map(|x|x.value) { #predefined #(#values,)* _ => return None @@ -113,7 +115,7 @@ impl ToTokens for ExposeArm { let Self(key, value) = self; let key = LitStr::new(&key, Span::call_site()); write_quote_to(out, quote! { - ::tengri::dsl::Value::Sym(#key) => self.#value() + Some(::tengri::dsl::Value::Sym(#key)) => self.#value() }) } } diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index c70cb31..b62d976 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -55,8 +55,10 @@ impl ToTokens for ViewDef { } } /// Generated by [tengri_proc]. - impl<'a> ::tengri::output::ViewContext<'a, #output> for #ident { - fn get_content_sym (&'a self, value: &Value<'a>) -> Option> { + impl<'state> ::tengri::output::ViewContext<'state, #output> for #ident { + fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>) + -> Option> + { match value { #(#exposed)* _ => panic!("expected Sym(content), got: {value:?}")