From 877b3447653f69f951f3bc0dad57de82350d866b Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 14 Mar 2025 23:44:13 +0200 Subject: [PATCH] edn -> dsl --- .forgejo/workflows/build.yaml | 2 +- Cargo.lock | 103 +++++++++++--------- Cargo.toml | 2 +- Justfile | 7 +- {edn => dsl}/Cargo.lock | 6 +- {edn => dsl}/Cargo.toml | 0 {edn => dsl}/README.md | 0 {edn => dsl}/proptest-regressions/iter.txt | 0 {edn => dsl}/proptest-regressions/token.txt | 0 {edn => dsl}/src/context.rs | 0 {edn => dsl}/src/error.rs | 0 {edn => dsl}/src/iter.rs | 2 +- {edn => dsl}/src/lib.rs | 0 {edn => dsl}/src/token.rs | 2 +- {edn => dsl}/test.edn | 0 input/Cargo.toml | 5 +- input/src/event_map.rs | 2 +- input/src/{input.rs => handle.rs} | 2 +- input/src/keymap.rs | 82 +++------------- input/src/lib.rs | 18 ++-- output/Cargo.toml | 6 +- output/README.md | 7 +- output/src/lib.rs | 5 +- output/src/measure.rs | 73 ++------------ output/src/op_align.rs | 11 ++- output/src/op_bsp.rs | 1 + output/src/op_cond.rs | 8 ++ output/src/op_transform.rs | 4 +- output/src/output.rs | 12 +++ output/src/view.rs | 10 ++ tengri/Cargo.toml | 4 +- tengri/src/lib.rs | 2 +- tui/Cargo.toml | 10 +- tui/examples/tui.rs | 3 +- tui/src/lib.rs | 33 +++++-- 35 files changed, 197 insertions(+), 225 deletions(-) rename {edn => dsl}/Cargo.lock (99%) rename {edn => dsl}/Cargo.toml (100%) rename {edn => dsl}/README.md (100%) rename {edn => dsl}/proptest-regressions/iter.txt (100%) rename {edn => dsl}/proptest-regressions/token.txt (100%) rename {edn => dsl}/src/context.rs (100%) rename {edn => dsl}/src/error.rs (100%) rename {edn => dsl}/src/iter.rs (99%) rename {edn => dsl}/src/lib.rs (100%) rename {edn => dsl}/src/token.rs (99%) rename {edn => dsl}/test.edn (100%) rename input/src/{input.rs => handle.rs} (98%) diff --git a/.forgejo/workflows/build.yaml b/.forgejo/workflows/build.yaml index deec9bf..e4d0c08 100644 --- a/.forgejo/workflows/build.yaml +++ b/.forgejo/workflows/build.yaml @@ -9,7 +9,7 @@ jobs: - run: whoami && pwd && ls -al - run: nix-shell --cores 4 --command 'cloc src/ && cloc .' .forgejo/workflows/build.nix - run: nix-shell --cores 4 --command 'rustup install nightly && cargo version -vv' .forgejo/workflows/build.nix - - run: nix-shell --cores 4 --command 'just cov-md' .forgejo/workflows/build.nix + - run: nix-shell --cores 4 --command 'just cov-md-ci' .forgejo/workflows/build.nix - run: nix-shell --cores 4 --command 'just doc' .forgejo/workflows/build.nix #- run: nix-shell --cores 4 --command 'just build-release' .forgejo/workflows/build.nix #- run: nix-shell -p docker --command "docker run --security-opt seccomp=unconfined -v $PWD:/volume xd009642/tarpaulin cargo tarpaulin --out Html --all-features" diff --git a/Cargo.lock b/Cargo.lock index 8ca8677..3ddc94d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -900,52 +900,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tek_edn" -version = "0.1.0" -dependencies = [ - "itertools 0.14.0", - "konst", - "proptest", - "tek_tui", - "thiserror", -] - -[[package]] -name = "tek_input" -version = "0.2.0" -dependencies = [ - "tek_edn", - "tek_tui", -] - -[[package]] -name = "tek_output" -version = "0.2.0" -dependencies = [ - "proptest", - "proptest-derive", - "tek_edn", - "tek_tui", -] - -[[package]] -name = "tek_tui" -version = "0.2.0" -dependencies = [ - "atomic_float", - "better-panic", - "crossterm", - "konst", - "palette", - "quanta", - "rand", - "ratatui", - "tek_edn", - "tek_input", - "tek_output", -] - [[package]] name = "tempfile" version = "3.17.1" @@ -960,6 +914,63 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "tengri" +version = "0.1.0" +dependencies = [ + "tengri_dsl", + "tengri_input", + "tengri_output", + "tengri_tui", +] + +[[package]] +name = "tengri_dsl" +version = "0.1.0" +dependencies = [ + "itertools 0.14.0", + "konst", + "proptest", + "tengri_tui", + "thiserror", +] + +[[package]] +name = "tengri_input" +version = "0.1.0" +dependencies = [ + "tengri_dsl", + "tengri_tui", +] + +[[package]] +name = "tengri_output" +version = "0.1.0" +dependencies = [ + "proptest", + "proptest-derive", + "tengri_dsl", + "tengri_tui", +] + +[[package]] +name = "tengri_tui" +version = "0.1.0" +dependencies = [ + "atomic_float", + "better-panic", + "crossterm", + "konst", + "palette", + "quanta", + "rand", + "ratatui", + "tengri", + "tengri_dsl", + "tengri_input", + "tengri_output", +] + [[package]] name = "thiserror" version = "2.0.12" diff --git a/Cargo.toml b/Cargo.toml index 82b0888..1334d06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = [ "./input", "./output", "./tui", - "./edn" + "./dsl" ] [profile.release] diff --git a/Justfile b/Justfile index 1943400..af61a41 100644 --- a/Justfile +++ b/Justfile @@ -2,11 +2,14 @@ covfig := "CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' RUSTDOCFLAGS='- grcov-binary := "--binary-path ./target/coverage/deps/" grcov-ignore := "--ignore-not-existing --ignore '../*' --ignore \"/*\" --ignore 'target/*'" cov: - {{covfig}} time cargo test -j4 --workspace --exclude jack --profile coverage + {{covfig}} time cargo test -j4 --workspace --profile coverage rm -rf target/coverage/html || true {{covfig}} time grcov . -s . {{grcov-binary}} {{grcov-ignore}} -t html -o target/coverage/html cov-md: - {{covfig}} time cargo test -j4 --workspace --exclude jack --profile coverage + {{covfig}} time cargo test -j4 --workspace --profile coverage + {{covfig}} time grcov . -s . {{grcov-binary}} {{grcov-ignore}} -t markdown | sort +cov-md-ci: + {{covfig}} time cargo test -j4 --workspace --profile coverage -- --skip test_tui_engine {{covfig}} time grcov . -s . {{grcov-binary}} {{grcov-ignore}} -t markdown | sort doc: cargo doc diff --git a/edn/Cargo.lock b/dsl/Cargo.lock similarity index 99% rename from edn/Cargo.lock rename to dsl/Cargo.lock index d29fc11..26585b0 100644 --- a/edn/Cargo.lock +++ b/dsl/Cargo.lock @@ -764,7 +764,7 @@ dependencies = [ ] [[package]] -name = "tengri_edn" +name = "tengri_dsl" version = "0.1.0" dependencies = [ "clojure-reader", @@ -781,7 +781,7 @@ version = "0.2.0" name = "tengri_output" version = "0.2.0" dependencies = [ - "tengri_edn", + "tengri_dsl", ] [[package]] @@ -793,7 +793,7 @@ dependencies = [ "palette", "rand", "ratatui", - "tengri_edn", + "tengri_dsl", "tengri_input", "tengri_output", ] diff --git a/edn/Cargo.toml b/dsl/Cargo.toml similarity index 100% rename from edn/Cargo.toml rename to dsl/Cargo.toml diff --git a/edn/README.md b/dsl/README.md similarity index 100% rename from edn/README.md rename to dsl/README.md diff --git a/edn/proptest-regressions/iter.txt b/dsl/proptest-regressions/iter.txt similarity index 100% rename from edn/proptest-regressions/iter.txt rename to dsl/proptest-regressions/iter.txt diff --git a/edn/proptest-regressions/token.txt b/dsl/proptest-regressions/token.txt similarity index 100% rename from edn/proptest-regressions/token.txt rename to dsl/proptest-regressions/token.txt diff --git a/edn/src/context.rs b/dsl/src/context.rs similarity index 100% rename from edn/src/context.rs rename to dsl/src/context.rs diff --git a/edn/src/error.rs b/dsl/src/error.rs similarity index 100% rename from edn/src/error.rs rename to dsl/src/error.rs diff --git a/edn/src/iter.rs b/dsl/src/iter.rs similarity index 99% rename from edn/src/iter.rs rename to dsl/src/iter.rs index bf1b19d..7f36289 100644 --- a/edn/src/iter.rs +++ b/dsl/src/iter.rs @@ -5,7 +5,7 @@ //! //! ``` //! let src = include_str!("../test.edn"); -//! let mut view = tengri_edn::TokenIter::new(src); +//! let mut view = tengri_dsl::TokenIter::new(src); //! assert_eq!(view.0.0, src); //! assert_eq!(view.peek(), view.0.peek()) //! ``` diff --git a/edn/src/lib.rs b/dsl/src/lib.rs similarity index 100% rename from edn/src/lib.rs rename to dsl/src/lib.rs diff --git a/edn/src/token.rs b/dsl/src/token.rs similarity index 99% rename from edn/src/token.rs rename to dsl/src/token.rs index 6ef6d55..467aeeb 100644 --- a/edn/src/token.rs +++ b/dsl/src/token.rs @@ -10,7 +10,7 @@ //! with slightly different parsing rules. //! * [Value::Num] is an unsigned integer literal. //!``` -//! use tengri_edn::{*, Value::*}; +//! use tengri_dsl::{*, Value::*}; //! let source = include_str!("../test.edn"); //! let mut view = TokenIter::new(source); //! assert_eq!(view.peek(), Some(Token { diff --git a/edn/test.edn b/dsl/test.edn similarity index 100% rename from edn/test.edn rename to dsl/test.edn diff --git a/input/Cargo.toml b/input/Cargo.toml index 4e65716..4ca1852 100644 --- a/input/Cargo.toml +++ b/input/Cargo.toml @@ -5,10 +5,11 @@ version = "0.1.0" description = "UI metaframework, input layer." [dependencies] -tengri_edn = { optional = true, path = "../edn" } +tengri_dsl = { optional = true, path = "../dsl" } [features] -edn = [ "tengri_edn" ] +dsl = [ "tengri_dsl" ] [dev-dependencies] tengri_tui = { path = "../tui" } +tengri_dsl = { path = "../dsl" } diff --git a/input/src/event_map.rs b/input/src/event_map.rs index 4a7e260..fc9644d 100644 --- a/input/src/event_map.rs +++ b/input/src/event_map.rs @@ -42,6 +42,7 @@ impl<'a, S, I: PartialEq, C> EventMap<'a, S, I, C> { input_to_command!($(<$lt>)? $Command: |$state: $State, input: $Input|$KEYS.handle($state, input)?); }; } + #[macro_export] macro_rules! input_to_command { (<$($l:lifetime),+> $Command:ty: |$state:ident:$State:ty, $input:ident:$Input:ty| $handler:expr) => { impl<$($l),+> InputToCommand<$Input, $State> for $Command { @@ -70,4 +71,3 @@ pub trait InputToCommand: Command + Sized { }) } } - diff --git a/input/src/input.rs b/input/src/handle.rs similarity index 98% rename from input/src/input.rs rename to input/src/handle.rs index c8b59e3..2a8fbcb 100644 --- a/input/src/input.rs +++ b/input/src/handle.rs @@ -46,7 +46,7 @@ impl> Handle for &mut H { } impl> Handle for Option { fn handle (&mut self, context: &E) -> Perhaps { - if let Some(ref mut handle) = self { + if let Some(handle) = self { handle.handle(context) } else { Ok(None) diff --git a/input/src/keymap.rs b/input/src/keymap.rs index 73b063e..629f8ac 100644 --- a/input/src/keymap.rs +++ b/input/src/keymap.rs @@ -1,13 +1,18 @@ use crate::*; + /// [Input] state that can be matched against a [Value]. pub trait AtomInput: Input { fn matches_atom (&self, token: &str) -> bool; } + +#[cfg(feature = "dsl")] pub trait KeyMap<'a> { /// Try to find a command that matches the current input event. fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option; } + +#[cfg(feature = "dsl")] impl<'a> KeyMap<'a> for SourceIter<'a> { fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option @@ -35,6 +40,8 @@ impl<'a> KeyMap<'a> for SourceIter<'a> { None } } + +#[cfg(feature = "dsl")] impl<'a> KeyMap<'a> for TokenIter<'a> { fn command , I: AtomInput> (&'a self, state: &'a S, input: &'a I) -> Option @@ -61,10 +68,16 @@ impl<'a> KeyMap<'a> for TokenIter<'a> { None } } + /// A [Command] that can be constructed from a [Token]. +#[cfg(feature = "dsl")] pub trait AtomCommand<'a, C>: TryFromAtom<'a, C> + Command {} + +#[cfg(feature = "dsl")] impl<'a, C, T: TryFromAtom<'a, C> + Command> AtomCommand<'a, C> for T {} + /** Implement `AtomCommand` for given `State` and `Command` */ +#[cfg(feature = "dsl")] #[macro_export] macro_rules! atom_command { ($Command:ty : |$state:ident:<$State:ident: $Trait:path>| { $(( // identifier @@ -156,72 +169,9 @@ impl<'a, C, T: TryFromAtom<'a, C> + Command> AtomCommand<'a, C> for T {} let $arg: $type = Context::<$type>::get_or_fail($state, $arg); }; } -//pub struct SourceKeyMap<'a>(&'a str); -//impl<'a> KeyMap for SourceKeyMap<'a> { - //fn command > (&self, state: &S, input: &AtomInput) -> Option { - //todo!(); - //None - //} -//} -//pub struct ParsedKeyMap<'a>(TokensIterator<'a>); -//impl<'a> KeyMap for ParsedKeyMap<'a> { - //fn command > (&self, state: &S, input: &AtomInput) -> Option { - //todo!(); - //None - //} -//} -//pub struct RefKeyMap<'a>(TokensIterator<'a>); -//impl<'a> KeyMap for RefKeyMap<'a> { - //fn command > (&self, state: &S, input: &AtomInput) -> Option { - //todo!(); - ////for token in self.0 { - ////match token?.kind() { - ////TokenKind::Exp => match atoms.as_slice() { - ////[key, command, args @ ..] => match (key.kind(), key.text()) { - ////(TokenKind::Sym, key) => { - ////if input.matches_atom(key) { - ////let command = C::from_atom(state, command, args); - ////if command.is_some() { - ////return command - ////} - ////} - ////}, - ////_ => panic!("invalid config: {item}") - ////}, - ////_ => panic!("invalid config: {item}") - ////} - ////_ => panic!("invalid config: {item}") - ////} - ////} - //None - //} -//} -//pub struct ArcKeyMap(Vec); -//impl KeyMap for ArcKeyMap { - //fn command > (&self, state: &S, input: &AtomInput) -> Option { - //for atom in self.0.iter() { - //match atom { - //ArcAtom::Exp(atoms) => match atoms.as_slice() { - //[key, command, args @ ..] => match (key.kind(), key.text()) { - //(TokenKind::Sym, key) => { - //if input.matches_atom(key) { - //let command = C::from_atom(state, command, args); - //if command.is_some() { - //return command - //} - //} - //}, - //_ => panic!("invalid config: {atom}") - //}, - //_ => panic!("invalid config: {atom}") - //} - //_ => panic!("invalid config: {atom}") - //} - //} - //None - //} -//} -#[cfg(test)] #[test] fn test_atom_keymap () -> Usually<()> { + +#[cfg(all(test, feature = "dsl"))] +#[test] fn test_atom_keymap () -> Usually<()> { let keymap = SourceIter::new(""); Ok(()) } diff --git a/input/src/lib.rs b/input/src/lib.rs index c6a0838..e27a8cb 100644 --- a/input/src/lib.rs +++ b/input/src/lib.rs @@ -1,16 +1,22 @@ #![feature(associated_type_defaults)] -mod input; pub use self::input::*; mod command; pub use self::command::*; +mod handle; pub use self::handle::*; mod keymap; pub use self::keymap::*; -//mod event_map; pub use self::event_map::*; -pub(crate) use ::tek_edn::*; + +#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; + /// Standard error trait. pub(crate) use std::error::Error; -/// Standard result type. -#[cfg(test)] pub(crate) type Usually = Result>; + /// Standard optional result type. pub(crate) type Perhaps = Result, Box>; -#[cfg(test)] #[test] fn test_stub_input () -> Usually<()> { + +/// Standard result type. +#[cfg(test)] +pub(crate) type Usually = Result>; + +#[cfg(test)] +#[test] fn test_stub_input () -> Usually<()> { use crate::*; struct TestInput(bool); enum TestEvent { Test1 } diff --git a/output/Cargo.toml b/output/Cargo.toml index 8cb2ebc..c43bf04 100644 --- a/output/Cargo.toml +++ b/output/Cargo.toml @@ -5,12 +5,14 @@ version = "0.1.0" description = "UI metaframework, output layer." [dependencies] -tengri_edn = { optional = true, path = "../edn" } +tengri_dsl = { optional = true, path = "../dsl" } [features] -edn = [ "tengri_edn" ] +dsl = [ "tengri_dsl" ] [dev-dependencies] +tengri = { path = "../tengri", features = [ "dsl", "tui" ] } tengri_tui = { path = "../tui" } +tengri_dsl = { path = "../dsl" } proptest = "^1" proptest-derive = "^0.5.1" diff --git a/output/README.md b/output/README.md index 4f575b6..dc19511 100644 --- a/output/README.md +++ b/output/README.md @@ -1,12 +1,11 @@ -# `tek_output` +# `tengri_output` ## free floating layout primitives this crate exposes several layout operators which work entirely in unsigned coordinates -and are generic over `tek_engine::Engine` -and `tek_engine::Content`. chiefly, they -are not dependent on rendering framework. +and are generic over the trait `Content`. +most importantly, they are not dependent on rendering framework. |operator|description| |-|-| diff --git a/output/src/lib.rs b/output/src/lib.rs index 0ff7a8f..9b40db2 100644 --- a/output/src/lib.rs +++ b/output/src/lib.rs @@ -1,4 +1,3 @@ -//#![feature(lazy_type_alias)] #![feature(step_trait)] #![feature(type_alias_impl_trait)] #![feature(impl_trait_in_assoc_type)] @@ -18,7 +17,9 @@ mod view; pub use self::view::*; pub(crate) use std::marker::PhantomData; pub(crate) use std::error::Error; -pub(crate) use ::tek_edn::*; + +#[cfg(feature = "dsl")] pub(crate) use ::tengri_dsl::*; + /// Standard result type. pub type Usually = Result>; /// Standard optional result type. diff --git a/output/src/measure.rs b/output/src/measure.rs index 784f0a2..2a1e415 100644 --- a/output/src/measure.rs +++ b/output/src/measure.rs @@ -1,10 +1,10 @@ use crate::*; use std::sync::{Arc, atomic::{AtomicUsize, Ordering::Relaxed}}; -//use ratatui::prelude::{Style, Color}; -// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small + pub trait HasSize { fn size (&self) -> &Measure; } + #[macro_export] macro_rules! has_size { (<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasSize<$E> for $Struct $(<$($L),*$($T),*>)? { @@ -12,6 +12,7 @@ pub trait HasSize { } } } + /// A widget that tracks its render width and height #[derive(Default)] pub struct Measure { @@ -19,12 +20,15 @@ pub struct Measure { pub x: Arc, pub y: Arc, } + +// TODO: 🡘 🡙 ←🡙→ indicator to expand window when too small impl Content for Measure { fn render (&self, to: &mut E) { self.x.store(to.area().w().into(), Relaxed); self.y.store(to.area().h().into(), Relaxed); } } + impl Clone for Measure { fn clone (&self) -> Self { Self { @@ -34,6 +38,7 @@ impl Clone for Measure { } } } + impl std::fmt::Debug for Measure { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("Measure") @@ -42,6 +47,7 @@ impl std::fmt::Debug for Measure { .finish() } } + impl Measure { pub fn new () -> Self { Self { @@ -79,66 +85,3 @@ impl Measure { Bsp::b(Fill::xy(self), item) } } -//#[cfg(test)] #[test] fn test_measure () { - //use tek_tui::*; - //let size: Measure = Measure::default().set_w(1usize).set_h(1usize).clone(); - //let size: Measure = (&Measure::new().set_wh(2usize, 1usize)).clone(); - //let _ = format!("{:?}", &size); - //let _ = size.wh(); - //let _ = size.format(); - //let _ = size.of(()); -//} - -///// A scrollable area. -//pub struct Scroll(pub F, pub Direction, pub u64, PhantomData) -//where - //E: Output, - //F: Send + Sync + Fn(&mut dyn FnMut(&dyn Content)->Usually<()>)->Usually<()>; - -//pub trait ContentDebug { - //fn debug > (other: W) -> DebugOverlay { - //DebugOverlay(Default::default(), other) - //} -//} - -//impl ContentDebug for E {} - -//impl Render for Measure { - //fn min_size (&self, _: [u16;2]) -> Perhaps<[u16;2]> { - //Ok(Some([0u16.into(), 0u16.into()].into())) - //} - //fn render (&self, to: &mut TuiOut) -> Usually<()> { - //self.set_w(to.area().w()); - //self.set_h(to.area().h()); - //Ok(()) - //} -//} - -//impl Measure { - //pub fn debug (&self) -> ShowMeasure { - //ShowMeasure(&self) - //} -//} - -//render!(Tui: |self: ShowMeasure<'a>|render(|to: &mut TuiOut|Ok({ - //let w = self.0.w(); - //let h = self.0.h(); - //to.blit(&format!(" {w} x {h} "), to.area.x(), to.area.y(), Some( - //Style::default().bold().italic().bg(Color::Rgb(255, 0, 255)).fg(Color::Rgb(0,0,0)) - //)) -//}))); - -//pub struct ShowMeasure<'a>(&'a Measure); - -//pub struct DebugOverlay>(PhantomData, pub W); - -//impl> Render for DebugOverlay { - //fn min_size (&self, to: [u16;2]) -> Perhaps<[u16;2]> { - //self.1.min_size(to) - //} - //fn render (&self, to: &mut TuiOut) -> Usually<()> { - //let [x, y, w, h] = to.area(); - //self.1.render(to)?; - //Ok(to.blit(&format!("{w}x{h}+{x}+{y}"), x, y, Some(Style::default().green()))) - //} -//} diff --git a/output/src/op_align.rs b/output/src/op_align.rs index 8ce3695..c2a719d 100644 --- a/output/src/op_align.rs +++ b/output/src/op_align.rs @@ -1,6 +1,6 @@ //! Aligns things to the container. Comes with caveats. //! ``` -//! use ::tek_tui::{*, tek_output::*}; +//! use ::tengri::{output::*, tui::*}; //! let area: [u16;4] = [10, 10, 20, 20]; //! fn test (area: [u16;4], item: &impl Content, expected: [u16;4]) { //! assert_eq!(Content::layout(item, area), expected); @@ -28,9 +28,12 @@ //! test(area, &Align::w(two_by_four()), [10, 19, 4, 2]); //! ``` use crate::*; + #[derive(Debug, Copy, Clone, Default)] pub enum Alignment { #[default] Center, X, Y, NW, N, NE, E, SE, S, SW, W } pub struct Align(Alignment, A); -try_from_expr!(<'a, E>: Align>: |state, iter| + +#[cfg(feature = "dsl")] +try_from_expr!(<'a, E>: Align>: |state, iter|{ if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { match key { "align/c"|"align/x"|"align/y"| @@ -56,7 +59,9 @@ try_from_expr!(<'a, E>: Align>: |state, iter| }, _ => return None } - }); + } +}); + impl Align { #[inline] pub fn c (a: A) -> Self { Self(Alignment::Center, a) } #[inline] pub fn x (a: A) -> Self { Self(Alignment::X, a) } diff --git a/output/src/op_bsp.rs b/output/src/op_bsp.rs index 5846750..f1a5a5d 100644 --- a/output/src/op_bsp.rs +++ b/output/src/op_bsp.rs @@ -16,6 +16,7 @@ impl, B: Content> Content for Bsp { } } } +#[cfg(feature = "dsl")] try_from_expr!(<'a, E>: Bsp, RenderBox<'a, E>>: |state, iter| { if let Some(Token { value: Value::Key(key), .. }) = iter.peek() { match key { diff --git a/output/src/op_cond.rs b/output/src/op_cond.rs index 7f5ad97..a52c06f 100644 --- a/output/src/op_cond.rs +++ b/output/src/op_cond.rs @@ -1,10 +1,14 @@ use crate::*; + /// Show an item only when a condition is true. pub struct When(pub bool, pub A); impl When { #[inline] pub fn new (c: bool, a: A) -> Self { Self(c, a) } } + /// Show one item if a condition is true and another if the condition is false pub struct Either(pub bool, pub A, pub B); impl Either { #[inline] pub fn new (c: bool, a: A, b: B) -> Self { Self(c, a, b) } } + +#[cfg(feature = "dsl")] try_from_expr!(<'a, E>: When>: |state, iter| { if let Some(Token { value: Value::Key("when"), .. }) = iter.peek() { let _ = iter.next().unwrap(); @@ -15,6 +19,8 @@ try_from_expr!(<'a, E>: When>: |state, iter| { return Some(Self(condition, content)) } }); + +#[cfg(feature = "dsl")] try_from_expr!(<'a, E>: Either, RenderBox<'a, E>>: |state, iter| { if let Some(Token { value: Value::Key("either"), .. }) = iter.peek() { let _ = iter.next().unwrap(); @@ -27,6 +33,7 @@ try_from_expr!(<'a, E>: Either, RenderBox<'a, E>>: |state, iter return Some(Self(condition, content, alternate)) } }); + impl> Content for When { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, item) = self; @@ -45,6 +52,7 @@ impl> Content for When { if *cond { item.render(to) } } } + impl, B: Render> Content for Either { fn layout (&self, to: E::Area) -> E::Area { let Self(cond, a, b) = self; diff --git a/output/src/op_transform.rs b/output/src/op_transform.rs index 5900d8c..a18a63a 100644 --- a/output/src/op_transform.rs +++ b/output/src/op_transform.rs @@ -3,7 +3,7 @@ //! //! Transform may also react to the [Area] provided. //! ``` -//! use ::tek_tui::{*, tek_output::*}; +//! use ::tengri::{output::*, tui::*}; //! let area: [u16;4] = [10, 10, 20, 20]; //! fn test (area: [u16;4], item: &impl Content, expected: [u16;4]) { //! assert_eq!(Content::layout(item, area), expected); @@ -30,6 +30,7 @@ macro_rules! transform_xy { #[inline] pub fn y (item: T) -> Self { Self::Y(item) } #[inline] pub fn xy (item: T) -> Self { Self::XY(item) } } + #[cfg(feature = "dsl")] impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { @@ -76,6 +77,7 @@ macro_rules! transform_xy_unit { #[inline] pub fn y (y: U, item: T) -> Self { Self::Y(y, item) } #[inline] pub 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>> TryFromAtom<'a, T> for $Enum> { fn try_from_expr (state: &'a T, iter: TokenIter<'a>) -> Option { diff --git a/output/src/output.rs b/output/src/output.rs index 6dcf9f1..af31438 100644 --- a/output/src/output.rs +++ b/output/src/output.rs @@ -20,6 +20,7 @@ pub trait Output: Send + Sync + Sized { #[inline] fn h (&self) -> Self::Unit { self.area().h() } #[inline] fn wh (&self) -> Self::Size { self.area().wh().into() } } + /// Renderable with dynamic dispatch. pub trait Render { /// Compute layout. @@ -31,6 +32,7 @@ pub trait Render { Box::new(self) as RenderBox<'a, E> } } + /// Most importantly, every [Content] is also a [Render]. /// /// However, the converse does not hold true. @@ -40,17 +42,21 @@ impl> Render for C { fn layout (&self, area: E::Area) -> E::Area { Content::layout(self, area) } fn render (&self, output: &mut E) { Content::render(self, output) } } + /// Opaque pointer to a renderable living on the heap. /// /// Return this from [Content::content] to use dynamic dispatch. pub type RenderBox<'a, E> = Box>; + /// You can render from a box. impl<'a, E: Output> Content for RenderBox<'a, E> { fn content (&self) -> impl Render { self.deref() } //fn boxed <'b> (self) -> RenderBox<'b, E> where Self: Sized + 'b { self } } + /// Opaque pointer to a renderable. pub type RenderDyn<'a, E> = dyn Render + Send + Sync + 'a; + /// You can render from an opaque pointer. impl<'a, E: Output> Content for &RenderDyn<'a, E> where Self: Sized { fn content (&self) -> impl Render { @@ -66,6 +72,7 @@ impl<'a, E: Output> Content for &RenderDyn<'a, E> where Self: Sized { Render::render(self.deref(), output) } } + /// Composable renderable with static dispatch. pub trait Content { /// Return a [Render]able of a specific type. @@ -75,17 +82,20 @@ pub trait Content { /// Draw to output. By default, delegates to [Self::content]. fn render (&self, output: &mut E) { self.content().render(output) } } + /// Every pointer to [Content] is a [Content]. impl> Content for &C { fn content (&self) -> impl Render { (*self).content() } fn layout (&self, area: E::Area) -> E::Area { (*self).layout(area) } fn render (&self, output: &mut E) { (*self).render(output) } } + /// The platonic ideal unit of [Content]: total emptiness at dead center (e=1vg^sqrt(-1)) impl Content for () { fn layout (&self, area: E::Area) -> E::Area { area.center().to_area_pos().into() } fn render (&self, _: &mut E) {} } + impl> Content for Option { fn content (&self) -> impl Render { self.as_ref() @@ -100,6 +110,7 @@ impl> Content for Option { .map(|content|content.render(output)); } } + /// Implement [Content] with composable content for a struct. #[macro_export] macro_rules! content { // Implement for all [Output]s. @@ -119,6 +130,7 @@ impl> Content for Option { } }; } + /// Implement [Content] with custom rendering for a struct. #[macro_export] macro_rules! render { (|$self:ident:$Struct:ident $(< diff --git a/output/src/view.rs b/output/src/view.rs index c199258..cfc0507 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,4 +1,5 @@ use crate::*; + #[macro_export] macro_rules! view { ($Output:ty: |$self:ident: $State:ty| $expr:expr; { $($sym:literal => $body:expr),* $(,)? @@ -23,7 +24,10 @@ use crate::*; // An ephemeral wrapper around view state and view description, // that is meant to be constructed and returned from [Content::content]. +#[cfg(feature = "dsl")] pub struct View<'a, T>(pub &'a T, pub SourceIter<'a>); + +#[cfg(feature = "dsl")] impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content for View<'a, T> { fn content (&self) -> impl Render { let iter = self.1.clone(); @@ -35,7 +39,9 @@ impl<'a, O: Output + 'a, T: ViewContext<'a, O>> Content for View<'a, T> { return None } } + // Provides components to the view. +#[cfg(feature = "dsl")] pub trait ViewContext<'a, E: Output + 'a>: Send + Sync + Context + Context @@ -67,6 +73,8 @@ pub trait ViewContext<'a, E: Output + 'a>: Send + Sync None } } + +#[cfg(feature = "dsl")] #[macro_export] macro_rules! try_delegate { ($s:ident, $atom:expr, $T:ty) => { if let Some(value) = <$T>::try_from_atom($s, $atom) { @@ -74,6 +82,8 @@ 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>> TryFromAtom<$l, T> for $Struct { diff --git a/tengri/Cargo.toml b/tengri/Cargo.toml index 818da4e..acbf27f 100644 --- a/tengri/Cargo.toml +++ b/tengri/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" description = "UI metaframework." [dependencies] -tengri_edn = { optional = true, path = "../edn" } +tengri_dsl = { optional = true, path = "../dsl" } tengri_input = { optional = true, path = "../input" } tengri_output = { optional = true, path = "../output" } tengri_tui = { optional = true, path = "../tui" } @@ -15,4 +15,4 @@ default = [ "input", "output", "tui" ] input = [ "tengri_input" ] output = [ "tengri_output" ] tui = [ "tengri_tui" ] -edn = [ "tengri_edn", "tengri_input/edn", "tengri_output/edn", "tengri_tui/edn" ] +dsl = [ "tengri_dsl", "tengri_input/dsl", "tengri_output/dsl", "tengri_tui/dsl" ] diff --git a/tengri/src/lib.rs b/tengri/src/lib.rs index 39979e5..c1c72a0 100644 --- a/tengri/src/lib.rs +++ b/tengri/src/lib.rs @@ -1,4 +1,4 @@ #[cfg(feature="output")] pub use ::tengri_output as output; #[cfg(feature="input")] pub use ::tengri_input as input; -#[cfg(feature="edn")] pub use ::tengri_edn as edn; +#[cfg(feature="dsl")] pub use ::tengri_dsl as dsl; #[cfg(feature="tui")] pub use ::tengri_tui as tui; diff --git a/tui/Cargo.toml b/tui/Cargo.toml index b9adc29..c41e740 100644 --- a/tui/Cargo.toml +++ b/tui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tengri_tui" -edition = "2021" +edition = "2024" version = "0.1.0" description = "UI metaframework, Ratatui backend." @@ -16,7 +16,11 @@ quanta = "0.12.3" tengri_input = { path = "../input" } tengri_output = { path = "../output" } -tengri_edn = { optional = true, path = "../edn" } +tengri_dsl = { optional = true, path = "../dsl" } + +[dev-dependencies] +tengri = { path = "../tengri", features = [ "dsl" ] } +tengri_dsl = { path = "../dsl" } [features] -edn = [ "tengri_edn", "tengri_input/edn", "tengri_output/edn" ] +dsl = [ "tengri_dsl", "tengri_input/dsl", "tengri_output/dsl" ] diff --git a/tui/examples/tui.rs b/tui/examples/tui.rs index 8eb5ba7..a52fbb4 100644 --- a/tui/examples/tui.rs +++ b/tui/examples/tui.rs @@ -1,5 +1,4 @@ -use tengri_tui::{*, tengri_input::*, tengri_output::*}; -use tengri_edn::*; +use tengri::{self, input::*, output::*, tui::*, dsl::*}; use std::sync::{Arc, RwLock}; use crossterm::event::{*, KeyCode::*}; use crate::ratatui::style::Color; diff --git a/tui/src/lib.rs b/tui/src/lib.rs index 34922d0..33f3a6f 100644 --- a/tui/src/lib.rs +++ b/tui/src/lib.rs @@ -6,29 +6,43 @@ mod tui_file; pub use self::tui_file::*; mod tui_input; pub use self::tui_input::*; mod tui_output; pub use self::tui_output::*; mod tui_perf; pub use self::tui_perf::*; -pub use ::tek_edn;// pub(crate) use ::tek_edn::*; -//pub use ::tek_time; pub(crate) use ::tek_time::*; -pub use ::tek_input; pub(crate) use tek_input::*; -pub use ::tek_output; pub(crate) use tek_output::*; -pub use ::better_panic; pub(crate) use better_panic::{Settings, Verbosity}; -pub use ::palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; -pub use ::crossterm; pub(crate) use crossterm::{ + +pub use ::tengri_input as input; +pub(crate) use ::tengri_input::*; + +pub use ::tengri_output as output; +pub(crate) use ::tengri_output::*; + +#[cfg(feature = "dsl")] +pub use ::tengri_dsl; +#[cfg(feature = "dsl")] +pub(crate) use ::tengri_dsl::*; + +pub(crate) use atomic_float::AtomicF64; + +pub use ::better_panic; pub(crate) use ::better_panic::{Settings, Verbosity}; + +pub use ::palette; pub(crate) use ::palette::{*, convert::*, okhsl::*}; + +pub use ::crossterm; pub(crate) use ::crossterm::{ ExecutableCommand, terminal::{EnterAlternateScreen, LeaveAlternateScreen, enable_raw_mode, disable_raw_mode}, event::{Event, KeyEvent, KeyCode, KeyModifiers, KeyEventKind, KeyEventState}, }; -pub use ::ratatui; pub(crate) use ratatui::{ + +pub use ::ratatui; pub(crate) use ratatui::{ prelude::{Color, Style, Buffer}, style::Modifier, backend::{Backend, CrosstermBackend, ClearType}, layout::{Size, Rect}, buffer::Cell }; + pub(crate) use std::sync::{Arc, RwLock, atomic::{AtomicBool, Ordering::*}}; pub(crate) use std::io::{stdout, Stdout}; pub(crate) use std::path::PathBuf; pub(crate) use std::ffi::OsString; -pub(crate) use atomic_float::AtomicF64; + #[macro_export] macro_rules! from { ($(<$($lt:lifetime),+>)?|$state:ident:$Source:ty|$Target:ty=$cb:expr) => { impl $(<$($lt),+>)? From<$Source> for $Target { @@ -36,6 +50,7 @@ pub(crate) use atomic_float::AtomicF64; } }; } + #[cfg(test)] #[test] fn test_tui_engine () -> Usually<()> { use crate::*; use std::sync::{Arc, RwLock};