wip: dsl, input, output, proc: more precise lifetimes
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-05-09 18:17:10 +03:00
parent 22d63eed9c
commit 5e09f5a4bb
11 changed files with 140 additions and 122 deletions

View file

@ -1,11 +1,17 @@
use crate::*; use crate::*;
pub trait TryFromDsl<'a, T>: Sized { pub trait TryFromDsl<'state, T>: Sized {
fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option<Self> { fn try_from_expr <'source: 'state> (
_state: &'state T, _iter: TokenIter<'source>
) -> Option<Self> {
None None
} }
fn try_from_atom (state: &'a T, value: Value<'a>) -> Option<Self> { fn try_from_atom <'source: 'state> (
if let Exp(0, iter) = value { return Self::try_from_expr(state, iter) } state: &'state T, value: Value<'source>
) -> Option<Self> {
if let Exp(0, iter) = value {
return Self::try_from_expr(state, iter.clone())
}
None None
} }
} }
@ -15,29 +21,20 @@ pub trait TryIntoDsl<T>: Sized {
} }
/// Map EDN tokens to parameters of a given type for a given context /// Map EDN tokens to parameters of a given type for a given context
pub trait Context<U>: Sized { pub trait Context<'state, U>: Sized {
fn get (&self, _atom: &Value) -> Option<U> { fn get <'source> (&'state self, _iter: &mut TokenIter<'source>) -> Option<U> {
None 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<U> {
(*self).get(iter)
} }
} }
impl<T: Context<U>, U> Context<U> for &T { impl<'state, T: Context<'state, U>, U> Context<'state, U> for Option<T> {
fn get (&self, dsl: &Value) -> Option<U> { fn get <'source> (&'state self, iter: &mut TokenIter<'source>) -> Option<U> {
(*self).get(dsl) self.as_ref().map(|s|s.get(iter)).flatten()
}
fn get_or_fail (&self, dsl: &Value) -> U {
(*self).get_or_fail(dsl)
}
}
impl<T: Context<U>, U> Context<U> for Option<T> {
fn get (&self, dsl: &Value) -> Option<U> {
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")
} }
} }

View file

@ -22,38 +22,38 @@
//!``` //!```
use crate::*; use crate::*;
#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'a> { #[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct Token<'source> {
pub source: &'a str, pub source: &'source str,
pub start: usize, pub start: usize,
pub length: 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, #[default] Nil,
Err(ParseError), Err(ParseError),
Num(usize), Num(usize),
Sym(&'a str), Sym(&'source str),
Key(&'a str), Key(&'source str),
Str(&'a str), Str(&'source str),
Exp(usize, TokenIter<'a>), Exp(usize, TokenIter<'source>),
} }
impl<'a> Token<'a> { impl<'source> Token<'source> {
pub const fn new (source: &'a str, start: usize, length: usize, value: Value<'a>) -> Self { pub const fn new (source: &'source str, start: usize, length: usize, value: Value<'source>) -> Self {
Self { source, start, length, value } Self { source, start, length, value }
} }
pub const fn end (&self) -> usize { pub const fn end (&self) -> usize {
self.start.saturating_add(self.length) 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) self.slice_source(self.source)
//str_range(self.source, self.start, self.end()) //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()) 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()) str_range(source, self.start.saturating_add(1), self.end())
} }
pub const fn value (&self) -> Value { pub const fn value (&self) -> Value {

View file

@ -2,9 +2,9 @@ use crate::*;
use std::marker::PhantomData; use std::marker::PhantomData;
/// A [Command] that can be constructed from a [Token]. /// A [Command] that can be constructed from a [Token].
pub trait DslCommand<'a, C>: TryFromDsl<'a, C> + Command<C> {} pub trait DslCommand<'state, C>: TryFromDsl<'state, C> + Command<C> {}
impl<'a, C, T: TryFromDsl<'a, C> + Command<C>> DslCommand<'a, C> for T {} impl<'state, C, T: TryFromDsl<'state, C> + Command<C>> DslCommand<'state, C> for T {}
/// [Input] state that can be matched against a [Value]. /// [Input] state that can be matched against a [Value].
pub trait DslInput: Input { pub trait DslInput: Input {
@ -12,14 +12,14 @@ pub trait DslInput: Input {
} }
/// A pre-configured mapping of input events to commands. /// 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. /// Try to find a command that matches the current input event.
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C>; fn command (&'state self, state: &'state S, input: &'state I) -> Option<C>;
} }
/// A [SourceIter] can be a [KeyMap]. /// A [SourceIter] can be a [KeyMap].
impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for SourceIter<'a> { impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for SourceIter<'state> {
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C> { fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
let mut iter = self.clone(); let mut iter = self.clone();
while let Some((token, rest)) = iter.next() { while let Some((token, rest)) = iter.next() {
iter = rest; 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]. /// A [TokenIter] can be a [KeyMap].
impl<'a, S, C: DslCommand<'a, S>, I: DslInput> KeyMap<'a, S, C, I> for TokenIter<'a> { impl<'state, S, C: DslCommand<'state, S>, I: DslInput> KeyMap<'state, S, C, I> for TokenIter<'state> {
fn command (&'a self, state: &'a S, input: &'a I) -> Option<C> { fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
let mut iter = self.clone(); let mut iter = self.clone();
while let Some(next) = iter.next() { while let Some(next) = iter.next() {
match 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> = Box<dyn Fn(&S)->bool + Send + Sync + 'a>; pub type InputLayerCond<'state, S> = Box<dyn Fn(&S)->bool + Send + Sync + 'state>;
/// A collection of pre-configured mappings of input events to commands, /// A collection of pre-configured mappings of input events to commands,
/// which may be made available subject to given conditions. /// 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 where
C: DslCommand<'a, S>, C: DslCommand<'state, S>,
I: DslInput, I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync M: KeyMap<'state, S, C, I> + Send + Sync
{ {
__: &'a PhantomData<(S, C, I)>, __: &'state PhantomData<(S, C, I)>,
pub layers: Vec<(InputLayerCond<'a, S>, M)>, 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 where
C: DslCommand<'a, S>, C: DslCommand<'state, S>,
I: DslInput, I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync M: KeyMap<'state, S, C, I> + Send + Sync
{ {
fn default () -> Self { fn default () -> Self {
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 where
C: DslCommand<'a, S>, C: DslCommand<'state, S>,
I: DslInput, I: DslInput,
M: KeyMap<'a, S, C, I> + Send + Sync M: KeyMap<'state, S, C, I> + Send + Sync
{ {
pub fn new (keymap: M) -> Self { pub fn new (keymap: M) -> Self {
Self::default().layer(keymap) Self::default().layer(keymap)
@ -115,21 +115,21 @@ where
self.add_layer_if(Box::new(|_|true), keymap); self.add_layer_if(Box::new(|_|true), keymap);
self 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.add_layer_if(condition, keymap);
self 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.layers.push((Box::new(condition), keymap));
self 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 where
C: DslCommand<'a, S>, C: DslCommand<'state, S>,
I: DslInput, 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> { fn fmt (&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(f, "[InputMap: {} layer(s)]", self.layers.len()); write!(f, "[InputMap: {} layer(s)]", self.layers.len());
@ -138,13 +138,13 @@ where
} }
/// An [InputMap] can be a [KeyMap]. /// 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 where
C: DslCommand<'a, S>, C: DslCommand<'state, S>,
I: DslInput, 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<C> { fn command (&'state self, state: &'state S, input: &'state I) -> Option<C> {
for (condition, keymap) in self.layers.iter() { for (condition, keymap) in self.layers.iter() {
if !condition(state) { if !condition(state) {
continue continue

View file

@ -36,7 +36,7 @@ pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W }
pub struct Align<A>(Alignment, A); pub struct Align<A>(Alignment, A);
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Align<RenderBox<'a, E>>: |state, iter|{ try_from_expr!(<'source, 'state, E>: Align<RenderBox<'state, E>>: |state, iter|{
if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { if let Some(Token { value: Value::Key(key), .. }) = iter.peek() {
match key { match key {
"align/c"|"align/x"|"align/y"| "align/c"|"align/x"|"align/y"|

View file

@ -17,7 +17,7 @@ impl<E: Output, A: Content<E>, B: Content<E>> Content<E> for Bsp<A, B> {
} }
} }
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Bsp<RenderBox<'a, E>, RenderBox<'a, E>>: |state, iter| { try_from_expr!(<'source, 'state, E>: Bsp<RenderBox<'state, E>, RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { if let Some(Token { value: Value::Key(key), .. }) = iter.peek() {
match key { match key {
"bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b" => { "bsp/n"|"bsp/s"|"bsp/e"|"bsp/w"|"bsp/a"|"bsp/b" => {

View file

@ -19,12 +19,12 @@ impl<A, B> Either<A, B> {
} }
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: When<RenderBox<'a, E>>: |state, iter| { try_from_expr!(<'source, 'state, E>: When<RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() { if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() {
let _ = iter.next().unwrap(); let _ = iter.next().unwrap();
let condition = iter.next().expect("no condition specified"); 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 = iter.next().expect("no content specified");
let content = if let Some(content) = state.get_content(&content.value) { let content = if let Some(content) = state.get_content(&content.value) {
@ -38,12 +38,12 @@ try_from_expr!(<'a, E>: When<RenderBox<'a, E>>: |state, iter| {
}); });
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
try_from_expr!(<'a, E>: Either<RenderBox<'a, E>, RenderBox<'a, E>>: |state, iter| { try_from_expr!(<'source, 'state, E>: Either<RenderBox<'state, E>, RenderBox<'state, E>>: |state, iter| {
if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() { if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() {
let _ = iter.next().unwrap(); let _ = iter.next().unwrap();
let condition = iter.next().expect("no condition specified"); 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 = iter.next().expect("no content specified");
let content = if let Some(content) = state.get_content(&content.value) { let content = if let Some(content) = state.get_content(&content.value) {

View file

@ -33,9 +33,11 @@ macro_rules! transform_xy {
#[inline] pub const fn xy (item: T) -> Self { Self::XY(item) } #[inline] pub const fn xy (item: T) -> Self { Self::XY(item) }
} }
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
for $Enum<RenderBox<'a, E>> { for $Enum<RenderBox<'state, E>> {
fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option<Self> { fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>)
-> Option<Self>
{
let mut iter = iter.clone(); let mut iter = iter.clone();
if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { if let Some(Token { value: Value::Key(k), .. }) = iter.peek() {
if k == $x || k == $y || k == $xy { 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) } #[inline] pub const fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) }
} }
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromDsl<'a, T> impl<'state, E: Output + 'state, T: ViewContext<'state, E>> TryFromDsl<'state, T>
for $Enum<E::Unit, RenderBox<'a, E>> { for $Enum<E::Unit, RenderBox<'state, E>> {
fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option<Self> { fn try_from_expr <'source: 'state> (state: &'state T, iter: TokenIter<'source>) -> Option<Self> {
let mut iter = iter.clone(); let mut iter = iter.clone();
if let Some(Token { value: Value::Key(k), .. }) = iter.peek() { if let Some(Token { value: Value::Key(k), .. }) = iter.peek() {
if k == $x || k == $y { if k == $x || k == $y {
let _ = iter.next().unwrap(); let _ = iter.next().unwrap();
let u = iter.next().expect("no unit specified"); let u = state.get(&mut iter).expect("no unit specified");
let u = get_value!(state => u); let c = state.get_content(&iter.next().expect("no content specified").value)
let c = iter.next().expect("no content specified"); .expect("no content provided");
let c = get_content!(state => c);
return Some(match k { return Some(match k {
$x => Self::x(u, c), $x => Self::x(u, c),
$y => Self::y(u, c), $y => Self::y(u, c),
@ -98,9 +99,10 @@ macro_rules! transform_xy_unit {
}) })
} else if k == $xy { } else if k == $xy {
let _ = iter.next().unwrap(); let _ = iter.next().unwrap();
let u = get_value!(state => iter.next().expect("no unit specified")); let u = state.get(&mut iter).expect("no unit specified");
let v = get_value!(state => iter.next().expect("no unit specified")); let v = state.get(&mut iter).expect("no unit specified");
let c = get_content!(state => iter.next().expect("no content specified")); let c = state.get_content(&iter.next().expect("no content specified").value)
.expect("no content provided");
return Some(Self::xy(u, v, c)) return Some(Self::xy(u, v, c))
} }
} }

View file

@ -45,34 +45,37 @@ impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content<O> for View<'a, T> {
// Provides components to the view. // Provides components to the view.
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
pub trait ViewContext<'a, E: Output + 'a>: Send + Sync pub trait ViewContext<'state, E: Output + 'state>: Send + Sync
+ Context<bool> + Context<'state, bool>
+ Context<usize> + Context<'state, usize>
+ Context<E::Unit> + Context<'state, E::Unit>
{ {
fn get_content (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>> { fn get_content <'source: 'state> (&'state self, value: &Value<'source>) -> Option<RenderBox<'state, E>> {
match value { match value {
Value::Sym(_) => self.get_content_sym(value), Value::Sym(_) => self.get_content_sym(value),
Value::Exp(_, _) => self.get_content_exp(value), Value::Exp(_, _) => self.get_content_exp(value),
_ => panic!("only :symbols and (expressions) accepted here") _ => panic!("only :symbols and (expressions) accepted here")
} }
} }
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>>; fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>)
fn get_content_exp (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, E>> { -> Option<RenderBox<'state, E>>;
try_delegate!(self, *value, When::<RenderBox<'a, E>>); fn get_content_exp <'source: 'state> (&'state self, value: &Value<'source>)
try_delegate!(self, *value, Either::<RenderBox<'a, E>, RenderBox<'a, E>>); -> Option<RenderBox<'state, E>>
try_delegate!(self, *value, Align::<RenderBox<'a, E>>); {
try_delegate!(self, *value, Bsp::<RenderBox<'a, E>, RenderBox<'a, E>>); try_delegate!(self, *value, When::<RenderBox<'state, E>>);
try_delegate!(self, *value, Fill::<RenderBox<'a, E>>); try_delegate!(self, *value, Either::<RenderBox<'state, E>, RenderBox<'state, E>>);
try_delegate!(self, *value, Fixed::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Align::<RenderBox<'state, E>>);
try_delegate!(self, *value, Min::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Bsp::<RenderBox<'state, E>, RenderBox<'state, E>>);
try_delegate!(self, *value, Max::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Fill::<RenderBox<'state, E>>);
try_delegate!(self, *value, Shrink::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Fixed::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Expand::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Min::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Push::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Max::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Pull::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Shrink::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Margin::<_, RenderBox<'a, E>>); try_delegate!(self, *value, Expand::<_, RenderBox<'state, E>>);
try_delegate!(self, *value, Padding::<_, RenderBox<'a, 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 None
} }
} }
@ -88,9 +91,20 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync
#[cfg(feature = "dsl")] #[cfg(feature = "dsl")]
#[macro_export] macro_rules! try_from_expr { #[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 { $lt_source:lifetime,
fn try_from_expr ($state: &$l T, $iter: TokenIter<'a>) -> Option<Self> { $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<Self> {
let mut $iter = $iter.clone(); let mut $iter = $iter.clone();
$body; $body;
None None

View file

@ -68,8 +68,10 @@ impl ToTokens for CommandDef {
} }
#block #block
/// Generated by [tengri_proc]. /// Generated by [tengri_proc].
impl<'a> TryFromDsl<'a, #target> for #enumeration { impl<'state> TryFromDsl<'state, #target> for #enumeration {
fn try_from_expr (state: &#target, iter: TokenIter) -> Option<Self> { fn try_from_expr <'source: 'state> (
state: &'state #target, iter: TokenIter<'source>
) -> Option<Self> {
let mut iter = iter.clone(); let mut iter = iter.clone();
match iter.next() { match iter.next() {
#(#matchers)* #(#matchers)*
@ -148,11 +150,10 @@ impl CommandArm {
for (arg, ty) in self.args() { for (arg, ty) in self.args() {
let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})", let take_err = LitStr::new(&format!("{}: missing argument \"{}\" ({})",
quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site()); 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()); quote!{#ident}, quote!{#arg}, quote!{#ty}), Span::call_site());
write_quote_to(&mut out, quote! { write_quote_to(&mut out, quote! {
#arg : Context::get(state, &iter.next().expect(#take_err).value) #arg: Context::get(state, &mut iter).expect(#give_err),
.expect(#give_err) ,
}); });
} }
out out

View file

@ -66,8 +66,8 @@ impl ToTokens for ExposeImpl {
let formatted_type = format!("{}", quote! { #t }); let formatted_type = format!("{}", quote! { #t });
let predefined = match formatted_type.as_str() { let predefined = match formatted_type.as_str() {
"bool" => quote! { "bool" => quote! {
::tengri::dsl::Value::Sym(":true") => true, Some(::tengri::dsl::Value::Sym(":true")) => true,
::tengri::dsl::Value::Sym(":false") => false, Some(::tengri::dsl::Value::Sym(":false")) => false,
}, },
"u8" | "u16" | "u32" | "u64" | "usize" | "u8" | "u16" | "u32" | "u64" | "usize" |
"i8" | "i16" | "i32" | "i64" | "isize" => { "i8" | "i16" | "i32" | "i64" | "isize" => {
@ -76,7 +76,7 @@ impl ToTokens for ExposeImpl {
Span::call_site() Span::call_site()
); );
quote! { 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)), .unwrap_or_else(|_|panic!(#num_err)),
} }
}, },
@ -85,9 +85,11 @@ impl ToTokens for ExposeImpl {
let values = variants.iter().map(ExposeArm::from); let values = variants.iter().map(ExposeArm::from);
write_quote_to(out, quote! { write_quote_to(out, quote! {
/// Generated by [tengri_proc]. /// Generated by [tengri_proc].
impl ::tengri::dsl::Context<#t> for #target { impl<'state> ::tengri::dsl::Context<'state, #t> for #target {
fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> { fn get <'source> (
Some(match dsl { &self, iter: &mut ::tengri::dsl::TokenIter<'source>
) -> Option<#t> {
Some(match iter.next().map(|x|x.value) {
#predefined #predefined
#(#values,)* #(#values,)*
_ => return None _ => return None
@ -113,7 +115,7 @@ impl ToTokens for ExposeArm {
let Self(key, value) = self; let Self(key, value) = self;
let key = LitStr::new(&key, Span::call_site()); let key = LitStr::new(&key, Span::call_site());
write_quote_to(out, quote! { write_quote_to(out, quote! {
::tengri::dsl::Value::Sym(#key) => self.#value() Some(::tengri::dsl::Value::Sym(#key)) => self.#value()
}) })
} }
} }

View file

@ -55,8 +55,10 @@ impl ToTokens for ViewDef {
} }
} }
/// Generated by [tengri_proc]. /// Generated by [tengri_proc].
impl<'a> ::tengri::output::ViewContext<'a, #output> for #ident { impl<'state> ::tengri::output::ViewContext<'state, #output> for #ident {
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, #output>> { fn get_content_sym <'source: 'state> (&'state self, value: &Value<'source>)
-> Option<RenderBox<'state, #output>>
{
match value { match value {
#(#exposed)* #(#exposed)*
_ => panic!("expected Sym(content), got: {value:?}") _ => panic!("expected Sym(content), got: {value:?}")