diff --git a/core/src/core_macros.rs b/core/src/core_macros.rs new file mode 100644 index 0000000..d5ada8f --- /dev/null +++ b/core/src/core_macros.rs @@ -0,0 +1,102 @@ +/// Define and reexport submodules. +#[macro_export] macro_rules! modules( + ($($($feat:literal?)? $name:ident),* $(,)?) => { $( + $(#[cfg(feature=$feat)])? mod $name; + $(#[cfg(feature=$feat)])? pub use self::$name::*; + )* }; + ($($($feat:literal?)? $name:ident $body:block),* $(,)?) => { $( + $(#[cfg(feature=$feat)])? mod $name $body + $(#[cfg(feature=$feat)])? pub use self::$name::*; + )* }; +); + +/// Define a trait an implement it for read-only wrapper types. */ +#[macro_export] macro_rules! flex_trait ( + ($Trait:ident $(<$($A:ident:$T:ident),+>)? $(:$dep:ident $(+$dep2:ident)*)? { + $(fn $fn:ident (&$self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* + }) => { + pub trait $Trait $(<$($A: $T),+>)? $(:$dep$(+$dep2)*)? { + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret $body)* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &_T_ { + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (**$self).$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<_T_> { + $(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + } + //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { + //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { + //if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) } + //})* + //} + //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> { + //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { (*$self).lock().unwrap().$fn($($arg),*) })* + //} + //impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> { + //$(fn $fn (&$self $(,$arg:$ty)*) -> $ret { $self.read().unwrap().$fn($($arg),*) })* + //} + }); + +/// Define a trait an implement it for various mutation-enabled wrapper types. */ +#[macro_export] macro_rules! flex_trait_mut ( + ($Trait:ident $(<$($A:ident:$T:ident),+>)? { + $(fn $fn:ident (&mut $self:ident $(, $arg:ident:$ty:ty)*) -> $ret:ty $body:block)* + })=>{ + pub trait $Trait $(<$($A: $T),+>)? { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { (*$self).$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for Option<_T_> { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { + if let Some(this) = $self { this.$fn($($arg),*) } else { Ok(None) } + })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Mutex<_T_> { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.get_mut().unwrap().$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::Mutex<_T_>> { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.lock().unwrap().$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::RwLock<_T_> { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })* + } + impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for ::std::sync::Arc<::std::sync::RwLock<_T_>> { + $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret { $self.write().unwrap().$fn($($arg),*) })* + } + }; +); + +/// Implement [`Debug`] in bulk. +#[macro_export] macro_rules! impl_debug(($($S:ty|$self:ident,$w:ident|$body:block)*)=>{ + $(impl std::fmt::Debug for $S { + fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body + })* }); + +/// Implement [`From`] in bulk. +#[macro_export] macro_rules! from( + ($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => { + impl $(<$($lt),+>)? From<$Source> for $Target { + fn from ($state:$Source) -> Self { $cb }}}; + ($($Struct:ty { $( + $(<$($l:lifetime),* $($T:ident$(:$U:ident)?),*>)? ($source:ident: $From:ty) $expr:expr + );+ $(;)? })*) => { $( + $(impl $(<$($l),* $($T$(:$U)?),*>)? From<$From> for $Struct { + fn from ($source: $From) -> Self { $expr } })+ )* }; ); + +/// Implement [Has]. +#[macro_export] macro_rules! has(($T:ty: |$self:ident : $S:ty| $x:expr) => { + impl Has<$T> for $S { + fn get (&$self) -> &$T { &$x } + fn get_mut (&mut $self) -> &mut $T { &mut $x } } };); + +/// Implement [MaybeHas]. +#[macro_export] macro_rules! maybe_has( + ($T:ty: |$self:ident : $S:ty| $x:block; $y:block $(;)?) => { + impl MaybeHas<$T> for $S { + fn get (&$self) -> Option<&$T> $x + fn get_mut (&mut $self) -> Option<&mut $T> $y } };); diff --git a/core/src/lib.rs b/core/src/lib.rs index b06b55e..0c63c05 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,38 +1,13 @@ +mod core_macros; pub(crate) use std::error::Error; /// Standard result type. pub type Usually = Result>; /// Standard optional result type. pub type Perhaps = Result, Box>; -/// Implement [`Debug`] in bulk. -#[macro_export] macro_rules! impl_debug(($($S:ty|$self:ident,$w:ident|$body:block)*)=>{ - $(impl std::fmt::Debug for $S { - fn fmt (&$self, $w: &mut std::fmt::Formatter) -> std::fmt::Result $body - })* }); -/// Implement [`From`] in bulk. -#[macro_export] macro_rules! from( - ($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => { - impl $(<$($lt),+>)? From<$Source> for $Target { - fn from ($state:$Source) -> Self { $cb }}}; - ($($Struct:ty { $( - $(<$($l:lifetime),* $($T:ident$(:$U:ident)?),*>)? ($source:ident: $From:ty) $expr:expr - );+ $(;)? })*) => { $( - $(impl $(<$($l),* $($T$(:$U)?),*>)? From<$From> for $Struct { - fn from ($source: $From) -> Self { $expr } })+ )* }; ); /// Type-dispatched `get` and `get_mut`. pub trait Has: Send + Sync { fn get (&self) -> &T; fn get_mut (&mut self) -> &mut T; } -/// Implement [Has]. -#[macro_export] macro_rules! has(($T:ty: |$self:ident : $S:ty| $x:expr) => { - impl Has<$T> for $S { - fn get (&$self) -> &$T { &$x } - fn get_mut (&mut $self) -> &mut $T { &mut $x } } };); /// Type-dispatched `get` and `get_mut` that return an [Option]-wrapped result. pub trait MaybeHas: Send + Sync { fn get (&self) -> Option<&T>; fn get_mut (&mut self) -> Option<&mut T>; } -/// Implement [MaybeHas]. -#[macro_export] macro_rules! maybe_has( - ($T:ty: |$self:ident : $S:ty| $x:block; $y:block $(;)?) => { - impl MaybeHas<$T> for $S { - fn get (&$self) -> Option<&$T> $x - fn get_mut (&mut $self) -> Option<&mut $T> $y } };); /// May compute a `RetVal` from `Args`. pub trait Eval { /// A custom operation on [Args] that may return [Result::Err] or [Option::None]. diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 530e4f5..979585d 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -1,46 +1,81 @@ //#![feature(adt_const_params)] //#![feature(type_alias_impl_trait)] +#![feature(if_let_guard)] #![feature(impl_trait_in_fn_trait_return)] #![feature(const_precise_live_drops)] extern crate const_panic; use const_panic::PanicFmt; use std::fmt::Debug; -pub(crate) use ::tengri_core::*; + pub(crate) use std::error::Error; +pub(crate) use std::sync::Arc; + pub(crate) use konst::string::{str_range, char_indices}; pub(crate) use thiserror::Error; +pub(crate) use ::tengri_core::*; + pub(crate) use self::DslError::*; -mod dsl_conv; pub use self::dsl_conv::*; -mod dsl_types; pub use self::dsl_types::*; -#[cfg(test)] mod dsl_test; +mod dsl_conv; +pub use self::dsl_conv::*; -pub trait Dsl: Debug + Send + Sync + Sized { - fn src (&self) -> &str; -} +mod dsl_parse; +pub(crate) use self::dsl_parse::*; +pub mod parse { pub use crate::dsl_parse::*; } -impl<'s> Dsl for &'s str { - fn src (&self) -> &str { self } -} +#[cfg(test)] +mod dsl_test; -impl Dsl for String { - fn src (&self) -> &str { self.as_str() } -} - -impl Dsl for std::sync::Arc { +flex_trait!(Dsl: Debug + Send + Sync + Sized { + fn src (&self) -> &str { + unreachable!("Dsl::src default impl") + } +}); +impl Dsl for Arc { + fn src (&self) -> &str { self.as_ref() } +} +impl<'s> Dsl for &'s str { fn src (&self) -> &str { self.as_ref() } } - impl Dsl for Option { fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } } -impl Dsl for &D { - fn src (&self) -> &str { (*self).src() } +impl DslExp for D {} +pub trait DslExp: Dsl { + fn exp (&self) -> DslPerhaps<&str> { + Ok(exp_peek(self.src())?) + } + fn head (&self) -> DslPerhaps<&str> { + Ok(peek(&self.src()[1..])?) + } + fn tail (&self) -> DslPerhaps<&str> { + Ok(if let Some((head_start, head_len)) = seek(&self.src()[1..])? { + peek(&self.src()[(1+head_start+head_len)..])? + } else { + None + }) + } } -impl Dsl for &mut D { - fn src (&self) -> &str { (**self).src() } +impl DslSym for D {} +pub trait DslSym: Dsl { + fn sym (&self) -> DslPerhaps<&str> { crate::parse::sym_peek(self.src()) } +} + +impl DslKey for D {} +pub trait DslKey: Dsl { + fn key (&self) -> DslPerhaps<&str> { crate::parse::key_peek(self.src()) } +} + +impl DslText for D {} +pub trait DslText: Dsl { + fn text (&self) -> DslPerhaps<&str> { crate::parse::text_peek(self.src()) } +} + +impl DslNum for D {} +pub trait DslNum: Dsl { + fn num (&self) -> DslPerhaps<&str> { crate::parse::num_peek(self.src()) } } /// DSL-specific result type. diff --git a/dsl/src/dsl_types.rs b/dsl/src/dsl_parse.rs similarity index 56% rename from dsl/src/dsl_types.rs rename to dsl/src/dsl_parse.rs index 0a5402d..25eef20 100644 --- a/dsl/src/dsl_types.rs +++ b/dsl/src/dsl_parse.rs @@ -6,13 +6,13 @@ macro_rules! iter_chars(($source:expr => |$i:ident, $c:ident|$val:expr)=>{ macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_length:ident)=>{ /// Find a slice corrensponding to a syntax token. - const fn $peek (source: &str) -> DslPerhaps<&str> { + pub const fn $peek (source: &str) -> DslPerhaps<&str> { match $seek(source) { Ok(Some((start, length))) => Ok(Some(str_range(source, start, start + length))), Ok(None) => Ok(None), Err(e) => Err(e) } } /// Find a start and length corresponding to a syntax token. - const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { + pub const fn $seek (source: &str) -> DslPerhaps<(usize, usize)> { match $seek_start(source) { Ok(Some(start)) => match $seek_length(str_range(source, start, source.len() - start)) { Ok(Some(length)) => Ok(Some((start, length))), @@ -22,31 +22,10 @@ macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_l Ok(None) => Ok(None), Err(e) => Err(e) } } }); -const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') } - -pub trait DslExp: Dsl { - fn exp (&self) -> DslPerhaps { - todo!(); - Ok(Some(self)) - } - fn exp_head (&self) -> DslPerhaps { - todo!(); - Ok(Some(self)) - } - fn exp_tail (&self) -> DslPerhaps { - todo!(); - Ok(Some(self)) - } - fn exp_next (&mut self) -> DslPerhaps { - todo!(); - Ok(Some(self)) - } -} -impl DslExp for D {} def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length); -const fn is_exp_start (c: char) -> bool { matches!(c, '(') } -const fn is_exp_end (c: char) -> bool { matches!(c, ')') } -const fn exp_seek_start (mut source: &str) -> DslPerhaps { +pub const fn is_exp_start (c: char) -> bool { matches!(c, '(') } +pub const fn is_exp_end (c: char) -> bool { matches!(c, ')') } +pub const fn exp_seek_start (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_exp_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { @@ -54,7 +33,7 @@ const fn exp_seek_start (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn exp_seek_length (mut source: &str) -> DslPerhaps { +pub const fn exp_seek_length (mut source: &str) -> DslPerhaps { let mut depth = 0; iter_chars!(source => |i, c| if is_exp_start(c) { depth += 1; @@ -70,13 +49,11 @@ const fn exp_seek_length (mut source: &str) -> DslPerhaps { Ok(None) } -pub trait DslSym: Dsl { fn sym (&self) -> DslPerhaps<&str> { sym_peek(self.src()) } } -impl DslSym for D {} def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length); -const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') } -const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') } -const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } -const fn sym_seek_start (mut source: &str) -> DslPerhaps { +pub const fn is_sym_start (c: char) -> bool { matches!(c, ':'|'@') } +pub const fn is_sym_char (c: char) -> bool { matches!(c, 'a'..='z'|'A'..='Z'|'0'..='9'|'-') } +pub const fn is_sym_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +pub const fn sym_seek_start (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_sym_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { @@ -84,7 +61,7 @@ const fn sym_seek_start (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn sym_seek_length (mut source: &str) -> DslPerhaps { +pub const fn sym_seek_length (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_sym_end(c) { return Ok(Some(i)) } else if !is_sym_char(c) { @@ -93,13 +70,11 @@ const fn sym_seek_length (mut source: &str) -> DslPerhaps { Ok(None) } -pub trait DslKey: Dsl { fn key (&self) -> DslPerhaps<&str> { key_peek(self.src()) } } -impl DslKey for D {} def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length); -const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') } -const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') } -const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } -const fn key_seek_start (mut source: &str) -> DslPerhaps { +pub const fn is_key_start (c: char) -> bool { matches!(c, '/'|'a'..='z') } +pub const fn is_key_char (c: char) -> bool { matches!(c, 'a'..='z'|'0'..='9'|'-'|'/') } +pub const fn is_key_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +pub const fn key_seek_start (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_key_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { @@ -107,7 +82,7 @@ const fn key_seek_start (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn key_seek_length (mut source: &str) -> DslPerhaps { +pub const fn key_seek_length (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_key_end(c) { return Ok(Some(i)) } else if !is_key_char(c) { @@ -116,12 +91,10 @@ const fn key_seek_length (mut source: &str) -> DslPerhaps { Ok(None) } -pub trait DslText: Dsl { fn text (&self) -> DslPerhaps<&str> { text_peek(self.src()) } } -impl DslText for D {} def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length); -const fn is_text_start (c: char) -> bool { matches!(c, '"') } -const fn is_text_end (c: char) -> bool { matches!(c, '"') } -const fn text_seek_start (mut source: &str) -> DslPerhaps { +pub const fn is_text_start (c: char) -> bool { matches!(c, '"') } +pub const fn is_text_end (c: char) -> bool { matches!(c, '"') } +pub const fn text_seek_start (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_text_start(c) { return Ok(Some(i)) } else if !is_whitespace(c) { @@ -129,15 +102,13 @@ const fn text_seek_start (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn text_seek_length (mut source: &str) -> DslPerhaps { +pub const fn text_seek_length (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_text_end(c) { return Ok(Some(i)) }); Ok(None) } -pub trait DslNum: Dsl { fn num (&self) -> DslPerhaps<&str> { num_peek(self.src()) } } -impl DslNum for D {} def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length); -const fn num_seek_start (mut source: &str) -> DslPerhaps { +pub const fn num_seek_start (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_digit(c) { return Ok(Some(i)); } else if !is_whitespace(c) { @@ -145,7 +116,7 @@ const fn num_seek_start (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn num_seek_length (mut source: &str) -> DslPerhaps { +pub const fn num_seek_length (mut source: &str) -> DslPerhaps { iter_chars!(source => |i, c| if is_num_end(c) { return Ok(Some(i)) } else if !is_digit(c) { @@ -153,8 +124,8 @@ const fn num_seek_length (mut source: &str) -> DslPerhaps { }); Ok(None) } -const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } -const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } +pub const fn is_digit (c: char) -> bool { matches!(c, '0'..='9') } +pub const fn is_num_end (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t'|')') } pub const fn to_number (digits: &str) -> Result { let mut iter = char_indices(digits); let mut value = 0; @@ -174,3 +145,39 @@ pub const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c)) }) } + +pub const fn peek (mut src: &str) -> DslPerhaps<&str> { + Ok(Some(match () { + _ if let Ok(Some(exp)) = exp_peek(src) => exp, + _ if let Ok(Some(sym)) = sym_peek(src) => sym, + _ if let Ok(Some(key)) = key_peek(src) => key, + _ if let Ok(Some(num)) = num_peek(src) => num, + _ if let Ok(Some(text)) = text_peek(src) => text, + _ => { + iter_chars!(src => |_i, c| if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + return Ok(None) + } + })) +} + +pub const fn seek (mut src: &str) -> DslPerhaps<(usize, usize)> { + Ok(Some(match () { + _ if let Ok(Some(exp)) = exp_seek(src) => exp, + _ if let Ok(Some(sym)) = sym_seek(src) => sym, + _ if let Ok(Some(key)) = key_seek(src) => key, + _ if let Ok(Some(num)) = num_seek(src) => num, + _ if let Ok(Some(text)) = text_seek(src) => text, + _ => { + iter_chars!(src => |_i, c| if !is_whitespace(c) { + return Err(Unexpected(c)) + }); + return Ok(None) + } + })) +} + +pub const fn is_whitespace (c: char) -> bool { + matches!(c, ' '|'\n'|'\r'|'\t') +} diff --git a/input/src/input_command.rs b/input/src/input.rs similarity index 50% rename from input/src/input_command.rs rename to input/src/input.rs index 6051277..7b451ee 100644 --- a/input/src/input_command.rs +++ b/input/src/input.rs @@ -1,5 +1,25 @@ use crate::*; +/// Event source +pub trait Input: Sized { + /// Type of input event + type Event; + /// Result of handling input + type Handled; // TODO: make this an Option> containing the undo + /// Currently handled event + fn event (&self) -> &Self::Event; + /// Whether component should exit + fn is_done (&self) -> bool; + /// Mark component as done + fn done (&self); +} + +flex_trait_mut!(Handle { + fn handle (&mut self, _input: &E) -> Perhaps { + Ok(None) + } +}); + pub trait Command: Send + Sync + Sized { fn execute (self, state: &mut S) -> Perhaps; fn delegate (self, state: &mut S, wrap: impl Fn(Self)->T) -> Perhaps diff --git a/input/src/input_dsl.rs b/input/src/input_dsl.rs index 0bec9a8..bb3c353 100644 --- a/input/src/input_dsl.rs +++ b/input/src/input_dsl.rs @@ -1,8 +1,10 @@ use crate::*; + /// Map of each event (e.g. key combination) to /// all command expressions bound to it by /// all loaded input layers. type EventMapImpl = BTreeMap>>; + /// A collection of input bindings. /// /// Each contained layer defines a mapping from input event to command invocation @@ -14,8 +16,27 @@ type EventMapImpl = BTreeMap>>; /// that .event()binding's value is returned. #[derive(Debug)] pub struct EventMap(EventMapImpl); + +/// An input binding. +#[derive(Debug, Clone)] +pub struct Binding { + pub command: C, + pub condition: Option, + pub description: Option>, + pub source: Option>, +} + +/// Input bindings are only returned if this evaluates to true +#[derive(Clone)] +pub struct Condition(Arcbool + Send + Sync>>); + +impl_debug!(Condition |self, w| { write!(w, "*") }); + /// Default is always empty map regardless if `E` and `C` implement [Default]. -impl Default for EventMap { fn default () -> Self { Self(Default::default()) } } +impl Default for EventMap { + fn default () -> Self { Self(Default::default()) } +} + impl EventMap { /// Create a new event map pub fn new () -> Self { @@ -28,7 +49,7 @@ impl EventMap { } /// Add a binding to an event map. pub fn add (&mut self, event: E, binding: Binding) -> &mut Self { - if (!self.0.contains_key(&event)) { + if !self.0.contains_key(&event) { self.0.insert(event.clone(), Default::default()); } self.0.get_mut(&event).unwrap().push(binding); @@ -54,33 +75,49 @@ impl EventMap { } /// Create event map from string. pub fn from_source (source: impl AsRef) -> Usually where E: From> { - Self::from_dsl(source.as_ref()) + Self::from_dsl(&mut source.as_ref()) } /// Create event map from DSL tokenizer. - pub fn from_dsl (mut dsl: impl Dsl) -> Usually where E: From> { + pub fn from_dsl <'s, > (dsl: &'s mut impl Dsl) -> Usually where E: From> { let mut map: Self = Default::default(); - while let Some(dsl) = dsl.exp_next()? { - if let Some(path) = dsl.text()? { - map.0.extend(Self::from_path(PathBuf::from(path.as_ref() as &str))?.0) - } else if dsl.exp_head()?.key()? == Some("if") { - todo!() - //map.add(sym.into(), Binding::from_dsl(dsl.exp_tail())?); - } else if let Some(sym) = dsl.exp_head()?.sym()? { - todo!() - //map.add(sym.into(), Binding::from_dsl(dsl.exp_tail())?); + if let Some(dsl) = dsl.exp()? { + let mut head = dsl.head()?; + let mut tail = dsl.tail()?; + loop { + if let Some(ref token) = head { + if let Some(ref text) = token.text()? { + map.0.extend(Self::from_path(PathBuf::from(text))?.0); + continue + } + //if let Some(ref exp) = token.exp()? { + ////_ if let Some(sym) = token.head()?.sym()?.as_ref() => { + ////todo!() + ////}, + ////_ if Some(&"if") == token.head()?.key()?.as_ref() => { + ////todo!() + ////}, + //return Err(format!("unexpected: {:?}", token.exp()).into()) + //} + return Err(format!("unexpected: {token:?}").into()) + } else { + break + } + if let Some(next) = tail { + head = next.head()?; + tail = next.tail()?; + } else { + break + } } + } else if let Some(text) = dsl.text()? { + todo!("load from file path") + } else { + todo!("return error for invalid input") } Ok(map) } } -/// An input binding. -#[derive(Debug)] -pub struct Binding { - pub command: C, - pub condition: Option, - pub description: Option>, - pub source: Option>, -} + impl Binding { fn from_dsl (dsl: impl Dsl) -> Usually { let mut command: Option = None; @@ -94,8 +131,6 @@ impl Binding { } } } -pub struct Condition(Boxbool + Send + Sync>); -impl_debug!(Condition |self, w| { write!(w, "*") }); fn unquote (x: &str) -> &str { let mut chars = x.chars(); diff --git a/input/src/input_handle.rs b/input/src/input_handle.rs deleted file mode 100644 index 2be276c..0000000 --- a/input/src/input_handle.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::*; -use std::sync::{Mutex, Arc, RwLock}; - -/// Event source -pub trait Input: Sized { - /// Type of input event - type Event; - /// Result of handling input - type Handled; // TODO: make this an Option> containing the undo - /// Currently handled event - fn event (&self) -> &Self::Event; - /// Whether component should exit - fn is_done (&self) -> bool; - /// Mark component as done - fn done (&self); -} - -/// Implement the [Handle] trait. -#[macro_export] macro_rules! handle { - (|$self:ident:$Struct:ty,$input:ident|$handler:expr) => { - impl ::tengri::input::Handle for $Struct { - fn handle (&mut $self, $input: &E) -> Perhaps { - $handler - } - } - }; - ($E:ty: |$self:ident:$Struct:ty,$input:ident|$handler:expr) => { - impl ::tengri::input::Handle<$E> for $Struct { - fn handle (&mut $self, $input: &$E) -> - Perhaps<<$E as ::tengri::input::Input>::Handled> - { - $handler - } - } - } -} - -/// Handle input -pub trait Handle { - fn handle (&mut self, _input: &E) -> Perhaps { - Ok(None) - } -} -impl> Handle for &mut H { - fn handle (&mut self, context: &E) -> Perhaps { - (*self).handle(context) - } -} -impl> Handle for Option { - fn handle (&mut self, context: &E) -> Perhaps { - if let Some(handle) = self { - handle.handle(context) - } else { - Ok(None) - } - } -} -impl Handle for Mutex where H: Handle { - fn handle (&mut self, context: &E) -> Perhaps { - self.get_mut().unwrap().handle(context) - } -} -impl Handle for Arc> where H: Handle { - fn handle (&mut self, context: &E) -> Perhaps { - self.lock().unwrap().handle(context) - } -} -impl Handle for RwLock where H: Handle { - fn handle (&mut self, context: &E) -> Perhaps { - self.write().unwrap().handle(context) - } -} -impl Handle for Arc> where H: Handle { - fn handle (&mut self, context: &E) -> Perhaps { - self.write().unwrap().handle(context) - } -} diff --git a/input/src/input_macros.rs b/input/src/input_macros.rs index ae5758c..e47b902 100644 --- a/input/src/input_macros.rs +++ b/input/src/input_macros.rs @@ -1,10 +1,30 @@ -/** Implement `Command` for given `State` and `handler` */ +/// Implement [Command] for given `State` and `handler` #[macro_export] macro_rules! command { ($(<$($l:lifetime),+>)?|$self:ident:$Command:ty,$state:ident:$State:ty|$handler:expr) => { - impl$(<$($l),+>)? Command<$State> for $Command { + impl$(<$($l),+>)? ::tengri::input::Command<$State> for $Command { fn execute ($self, $state: &mut $State) -> Perhaps { Ok($handler) } } }; } + +/// Implement [Handle] for given `State` and `handler`. +#[macro_export] macro_rules! handle { + (|$self:ident:$State:ty,$input:ident|$handler:expr) => { + impl ::tengri::input::Handle for $State { + fn handle (&mut $self, $input: &E) -> Perhaps { + $handler + } + } + }; + ($E:ty: |$self:ident:$State:ty,$input:ident|$handler:expr) => { + impl ::tengri::input::Handle<$E> for $State { + fn handle (&mut $self, $input: &$E) -> + Perhaps<<$E as ::tengri::input::Input>::Handled> + { + $handler + } + } + } +} diff --git a/input/src/input_test.rs b/input/src/input_test.rs new file mode 100644 index 0000000..8373435 --- /dev/null +++ b/input/src/input_test.rs @@ -0,0 +1,28 @@ +use crate::*; + +#[test] fn test_stub_input () -> Usually<()> { + use crate::*; + struct TestInput(bool); + enum TestEvent { Test1 } + impl Input for TestInput { + type Event = TestEvent; + type Handled = (); + fn event (&self) -> &Self::Event { + &TestEvent::Test1 + } + fn is_done (&self) -> bool { + self.0 + } + fn done (&self) {} + } + let _ = TestInput(true).event(); + assert!(TestInput(true).is_done()); + assert!(!TestInput(false).is_done()); + Ok(()) +} + +#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> { + let _keymap = CstIter::new(""); + Ok(()) +} + diff --git a/input/src/lib.rs b/input/src/lib.rs index 0779092..f219c64 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -3,41 +3,14 @@ pub(crate) use std::fmt::Debug; pub(crate) use std::sync::Arc; -pub(crate) use std::collections::{BTreeMap, HashMap}; +pub(crate) use std::collections::BTreeMap; pub(crate) use std::path::{Path, PathBuf}; pub(crate) use std::fs::exists; pub(crate) use tengri_core::*; mod input_macros; -mod input_command; pub use self::input_command::*; -mod input_handle; pub use self::input_handle::*; - +mod input; pub use self::input::*; #[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; #[cfg(feature = "dsl")] mod input_dsl; #[cfg(feature = "dsl")] pub use self::input_dsl::*; - -#[cfg(test)] #[test] fn test_stub_input () -> Usually<()> { - use crate::*; - struct TestInput(bool); - enum TestEvent { Test1 } - impl Input for TestInput { - type Event = TestEvent; - type Handled = (); - fn event (&self) -> &Self::Event { - &TestEvent::Test1 - } - fn is_done (&self) -> bool { - self.0 - } - fn done (&self) {} - } - let _ = TestInput(true).event(); - assert!(TestInput(true).is_done()); - assert!(!TestInput(false).is_done()); - Ok(()) -} - -#[cfg(all(test, feature = "dsl"))] #[test] fn test_dsl_keymap () -> Usually<()> { - let _keymap = CstIter::new(""); - Ok(()) -} +#[cfg(test)] mod input_test; diff --git a/output/src/ops.rs b/output/src/ops.rs index 846c86c..bdbcff0 100644 --- a/output/src/ops.rs +++ b/output/src/ops.rs @@ -373,7 +373,7 @@ transform_xy_unit!("padding/x" "padding/y" "padding/xy"|self: Padding, area|{ /// the layout elements that are provided by this crate. #[cfg(feature = "dsl")] mod ops_dsl { use crate::*; - use ::tengri_dsl::*; + //use ::tengri_dsl::*; //macro_rules! dsl { //($( diff --git a/proc/src/proc_flex.rs b/proc/src/proc_flex.rs new file mode 100644 index 0000000..039dd7d --- /dev/null +++ b/proc/src/proc_flex.rs @@ -0,0 +1,2 @@ +// TODO: #[derive(Flex, FlexMut, FlexOption, FlexResult ... ?)] +// better derive + annotations diff --git a/proc/src/proc_view.rs b/proc/src/proc_view.rs index bd44c96..13a5d5c 100644 --- a/proc/src/proc_view.rs +++ b/proc/src/proc_view.rs @@ -56,13 +56,13 @@ impl ViewDef { let builtins = builtins_with_boxes_output(quote! { #output }) .map(|(builtin, builtin_ty)|match builtin { Single(name) => quote! { - if dsl.exp_head()?.key()? == Some(#name) { + if dsl.head()?.key()? == Some(#name) { return Ok(Some(#builtin_ty::from_dsl_or_else(self, dsl, ||format!("failed to load builtin").into())?.boxed())) } }, Prefix(name) => quote! { - if let Some(key) = dsl.exp_head()?.key()? && key.starts_with(#name) { + if let Some(key) = dsl.head()?.key()? && key.starts_with(#name) { return Ok(Some(#builtin_ty::from_dsl_or_else(self, dsl, ||format!("failed to load builtin").into())?.boxed())) }