From 9f7d0efda5a71dfbb55c55dbff32c348b55dd870 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Tue, 29 Jul 2025 17:03:48 +0300 Subject: [PATCH] core, input, output, dsl: factor away 'flexi' traits --- core/src/core_macros.rs | 46 +++- dsl/src/dsl.rs | 84 ++++--- dsl/src/{dsl_types.rs => dsl_parse.rs} | 280 +++++++++++------------ input/src/{input_command.rs => input.rs} | 20 ++ input/src/input_dsl.rs | 93 +++++--- input/src/input_handle.rs | 82 ------- input/src/input_macros.rs | 24 +- input/src/input_test.rs | 28 +++ input/src/lib.rs | 33 +-- output/src/ops.rs | 2 +- proc/src/proc_flex.rs | 2 + 11 files changed, 355 insertions(+), 339 deletions(-) rename dsl/src/{dsl_types.rs => dsl_parse.rs} (63%) rename input/src/{input_command.rs => input.rs} (50%) delete mode 100644 input/src/input_handle.rs create mode 100644 input/src/input_test.rs create mode 100644 proc/src/proc_flex.rs diff --git a/core/src/core_macros.rs b/core/src/core_macros.rs index b819764..d5ada8f 100644 --- a/core/src/core_macros.rs +++ b/core/src/core_macros.rs @@ -1,5 +1,3 @@ -use crate::*; - /// Define and reexport submodules. #[macro_export] macro_rules! modules( ($($($feat:literal?)? $name:ident),* $(,)?) => { $( @@ -12,7 +10,37 @@ use crate::*; )* }; ); -/// Define a trait for various wrapper types. */ +/// 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)* @@ -20,24 +48,24 @@ use crate::*; pub trait $Trait $(<$($A: $T),+>)? { $(fn $fn (&mut $self $(,$arg:$ty)*) -> $ret $body)* } - impl<$($($A: $T,)+)? _T_: $Trait $(<$($A),+>)?> $Trait $(<$($A),+>)? for &mut _T_ { + 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_> { + 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_> { + 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_>> { + 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_> { + 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_>> { + 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),*) })* } }; diff --git a/dsl/src/dsl.rs b/dsl/src/dsl.rs index 8f83589..979585d 100644 --- a/dsl/src/dsl.rs +++ b/dsl/src/dsl.rs @@ -6,48 +6,78 @@ 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 Dsl for &D { - fn src (&self) -> &str { (*self).src() } +impl<'s> Dsl for &'s str { + fn src (&self) -> &str { self.as_ref() } } - -impl Dsl for &mut D { - fn src (&self) -> &str { (**self).src() } -} - -impl Dsl for std::sync::Arc { - fn src (&self) -> &str { (*self).src() } -} - impl Dsl for Option { fn src (&self) -> &str { if let Some(dsl) = self { dsl.src() } else { "" } } } +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 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. pub type DslResult = Result; diff --git a/dsl/src/dsl_types.rs b/dsl/src/dsl_parse.rs similarity index 63% rename from dsl/src/dsl_types.rs rename to dsl/src/dsl_parse.rs index c9d69a1..25eef20 100644 --- a/dsl/src/dsl_types.rs +++ b/dsl/src/dsl_parse.rs @@ -1,16 +1,18 @@ use crate::*; + macro_rules! iter_chars(($source:expr => |$i:ident, $c:ident|$val:expr)=>{ while let Some((($i, $c), next)) = char_indices($source).next() { $source = next.as_str(); $val } }); + 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))), @@ -20,6 +22,130 @@ macro_rules! def_peek_seek(($peek:ident, $seek:ident, $seek_start:ident, $seek_l Ok(None) => Ok(None), Err(e) => Err(e) } } }); +def_peek_seek!(exp_peek, exp_seek, exp_seek_start, exp_seek_length); +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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; + } else if is_exp_end(c) { + if depth == 0 { + return Err(Unexpected(c)) + } else if depth == 1 { + return Ok(Some(i)) + } else { + depth -= 1; + } + }); + Ok(None) +} + +def_peek_seek!(sym_peek, sym_seek, sym_seek_start, sym_seek_length); +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} + +def_peek_seek!(key_peek, key_seek, key_seek_start, key_seek_length); +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} + +def_peek_seek!(text_peek, text_seek, text_seek_start, text_seek_length); +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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) +} + +def_peek_seek!(num_peek, num_seek, num_seek_start, num_seek_length); +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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) { + return Err(Unexpected(c)) + }); + Ok(None) +} +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; + while let Some(((_, c), next)) = iter.next() { + match to_digit(c) { + Ok(digit) => value = 10 * value + digit, + Err(e) => return Err(e), + } + iter = next; + } + Ok(value) +} +pub const fn to_digit (c: char) -> Result { + Ok(match c { + '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, + '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, + _ => 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, @@ -52,152 +178,6 @@ pub const fn seek (mut src: &str) -> DslPerhaps<(usize, usize)> { })) } -const fn is_whitespace (c: char) -> bool { matches!(c, ' '|'\n'|'\r'|'\t') } -pub trait DslExp<'s>: Dsl + From<&'s str> { - fn exp (&'s self) -> DslPerhaps { - Ok(exp_peek(self.src())?.map(Into::into)) - } - fn head (&'s self) -> DslPerhaps { - Ok(peek(&self.src()[1..])?.map(Into::into)) - } - fn tail (&'s self) -> DslPerhaps { - Ok(if let Some((head_start, head_len)) = seek(&self.src()[1..])? { - peek(&self.src()[(1+head_start+head_len)..])?.map(Into::into) - } else { - None - }) - } -} -//impl<'s, D: DslExp<'s>> DslExp<'s> for Option {} -impl<'s, D: Dsl + From<&'s str>> DslExp<'s> 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 { - iter_chars!(source => |i, c| if is_exp_start(c) { - return Ok(Some(i)) - } else if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(None) -} -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; - } else if is_exp_end(c) { - if depth == 0 { - return Err(Unexpected(c)) - } else if depth == 1 { - return Ok(Some(i)) - } else { - depth -= 1; - } - }); - 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 { - iter_chars!(source => |i, c| if is_sym_start(c) { - return Ok(Some(i)) - } else if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(None) -} -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) { - return Err(Unexpected(c)) - }); - 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 { - iter_chars!(source => |i, c| if is_key_start(c) { - return Ok(Some(i)) - } else if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(None) -} -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) { - return Err(Unexpected(c)) - }); - 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 { - iter_chars!(source => |i, c| if is_text_start(c) { - return Ok(Some(i)) - } else if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(None) -} -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 { - iter_chars!(source => |i, c| if is_digit(c) { - return Ok(Some(i)); - } else if !is_whitespace(c) { - return Err(Unexpected(c)) - }); - Ok(None) -} -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) { - return Err(Unexpected(c)) - }); - 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 to_number (digits: &str) -> Result { - let mut iter = char_indices(digits); - let mut value = 0; - while let Some(((_, c), next)) = iter.next() { - match to_digit(c) { - Ok(digit) => value = 10 * value + digit, - Err(e) => return Err(e), - } - iter = next; - } - Ok(value) -} -pub const fn to_digit (c: char) -> Result { - Ok(match c { - '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, - '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, - _ => return Err(Unexpected(c)) - }) +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 8e87506..855272d 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,26 @@ type EventMapImpl = BTreeMap>>; /// that .event()binding's value is returned. #[derive(Debug)] pub struct EventMap(EventMapImpl); + +/// An input binding. +#[derive(Debug)] +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 +pub struct Condition(Boxbool + 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 +48,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); @@ -57,47 +77,46 @@ impl EventMap { Self::from_dsl(&mut source.as_ref()) } /// Create event map from DSL tokenizer. - pub fn from_dsl <'s, D: DslExp<'s> + 's> (dsl: &'s mut D) -> Usually where E: From> { + pub fn from_dsl <'s, > (dsl: &'s mut impl Dsl) -> Usually where E: From> { let mut map: Self = Default::default(); - let mut head: Option = dsl.head()?; - let mut tail: Option = 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(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 } - //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; @@ -111,8 +130,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 ed13cdc..0000000 --- a/input/src/input_handle.rs +++ /dev/null @@ -1,82 +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 - } - } - } -} - -flex_trait_mut!(Handle { - fn handle (&mut self, _input: &E) -> Perhaps { - Ok(None) - } -}); - -//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