diff --git a/edn/src/token.rs b/edn/src/token.rs index 31c943c7..27a342eb 100644 --- a/edn/src/token.rs +++ b/edn/src/token.rs @@ -5,7 +5,6 @@ use konst::string::{split_at, str_range, char_indices}; use itertools::join; use self::ParseError::*; use self::TokenKind::*; -type TokenResult<'a> = Result, ParseError>; #[derive(Debug)] pub enum ParseError { Unimplemented, Empty, Incomplete, Unexpected(char), Code(u8), } @@ -31,6 +30,7 @@ macro_rules! iterate { } } #[derive(Copy, Clone, PartialEq)] pub struct TokensIterator<'a>(&'a str); +type TokenResult<'a> = Result, ParseError>; impl<'a> Iterator for TokensIterator<'a> { type Item = TokenResult<'a>; fn next (&mut self) -> Option> { self.next_mut().map(|(result, _)|result) } @@ -47,6 +47,13 @@ impl<'a> TokensIterator<'a> { Self::next_mut(&mut self) } pub const fn next_mut (&mut self) -> Option<(TokenResult<'a>, Self)> { + match self.peek() { + Some(Ok(token)) => Some((Ok(token), self.split(token.end()))), + Some(Err((l, e))) => Some((Err(e), self.split(l))), + None => None + } + } + pub const fn peek (&self) -> Option, (usize, ParseError)>> { let mut token: Token<'a> = Token::new(self.0, Nil, 0, 0, 0); iterate!(char_indices(self.0) => (index, c) => token = match token.kind() { Nil => match c { @@ -55,36 +62,39 @@ impl<'a> TokensIterator<'a> { '0'..='9' => Token::new(self.0, Num, index, 1, 0), '/'|'a'..='z' => Token::new(self.0, Key, index, 1, 0), ' '|'\n'|'\r'|'\t' => token.grow(), - _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + _ => return Some(Err((token.end(), Unexpected(c)))) }, Num => match c { '0'..='9' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), - _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), + _ => return Some(Err((token.end(), Unexpected(c)))) }, Sym => match c { 'a'..='z'|'0'..='9'|'-' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), - _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), + _ => return Some(Err((token.end(), Unexpected(c)))) }, Key => match c { 'a'..='z'|'0'..='9'|'-'|'/' => token.grow(), - ' '|'\n'|'\r'|'\t' => return Some((Ok(token), self.split(token.end()))), - _ => return Some((Err(Unexpected(c)), self.split(token.end()))) + ' '|'\n'|'\r'|'\t' => return Some(Ok(token)), + _ => return Some(Err((token.end(), Unexpected(c)))) }, Exp => match token.depth { - 0 => return Some((Ok(token), Self(split_at(self.0, token.end()).1))), + 0 => return Some(Ok(token)), _ => match c { ')' => match token.grow_out() { Ok(token) => token, - Err(e) => return Some((Err(e), self.split(token.end()))) + Err(e) => return Some(Err((token.end(), e))) }, '(' => token.grow_in(), _ => token.grow(), } }, }); - match token.kind() { Nil => None, _ => Some((Err(ParseError::Incomplete), self.split(token.end()))) } + match token.kind() { + Nil => None, + _ => Some(Err((token.end(), ParseError::Incomplete))) + } } } #[derive(Debug, Copy, Clone, Default, PartialEq)] pub enum TokenKind { @@ -154,7 +164,7 @@ const fn to_digit (c: char) -> Result { _ => return Err(Unexpected(c)) }) } -pub trait Atom: Sized { +pub trait Atom: Sized + Send + Sync { fn kind (&self) -> TokenKind; fn text (&self) -> &str; fn num (&self) -> usize; @@ -296,27 +306,27 @@ impl<'a> Display for ArcAtom { } } /// Map EDN tokens to parameters of a given type for a given context -pub trait Context<'a, U>: Sized { - fn get (&'a self, _atom: &'a impl Atom) -> Option { +pub trait Context: Sized { + fn get (&self, _atom: &impl Atom) -> Option { None } - fn get_or_fail (&'a self, atom: &'a impl Atom) -> U { + fn get_or_fail (&self, atom: &impl Atom) -> U { self.get(atom).expect("no value") } } -impl<'a, T: Context<'a, U>, U> Context<'a, U> for &T { - fn get (&'a self, atom: &'a impl Atom) -> Option { +impl, U> Context for &T { + fn get (&self, atom: &impl Atom) -> Option { (*self).get(atom) } - fn get_or_fail (&'a self, atom: &'a impl Atom) -> U { + fn get_or_fail (&self, atom: &impl Atom) -> U { (*self).get_or_fail(atom) } } -impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { - fn get (&'a self, atom: &'a impl Atom) -> Option { +impl, U> Context for Option { + fn get (&self, atom: &impl Atom) -> Option { self.as_ref().map(|s|s.get(atom)).flatten() } - fn get_or_fail (&'a self, atom: &'a impl Atom) -> U { + fn get_or_fail (&self, atom: &impl Atom) -> U { self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") } } @@ -324,8 +334,8 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { #[macro_export] macro_rules! provide { // Provide a value to the EDN template ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<'a> Context<'a, $type> for $State { - fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> { + impl Context<$type> for $State { + fn get (&$self, atom: &impl Atom) -> Option<$type> { Some(match (atom.kind(), atom.text()) { $((TokenKind::Sym, $pat) => $expr,)* _ => return None @@ -336,7 +346,7 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { // Provide a value more generically ($lt:lifetime: $type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<$lt> Context<$lt, $type> for $State { - fn get (&$lt $self, atom: &$lt impl Atom) -> Option<$type> { + fn get (&$lt $self, atom: &impl Atom) -> Option<$type> { Some(match (atom.kind(), atom.text()) { $((TokenKind::Sym, $pat) => $expr,)* _ => return None @@ -351,8 +361,8 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { #[macro_export] macro_rules! provide_num { // Provide a value that may also be a numeric literal in the EDN, to a generic implementation. ($type:ty:|$self:ident:<$T:ident:$Trait:path>|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<'a, $T: $Trait> Context<'a, $type> for $T { - fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> { + impl<$T: $Trait> Context<$type> for $T { + fn get (&$self, atom: &impl Atom) -> Option<$type> { Some(match (atom.kind(), atom.text()) { $((TokenKind::Sym, $pat) => $expr,)* (TokenKind::Num, _) => atom.num() as $type, @@ -363,8 +373,8 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { }; // Provide a value that may also be a numeric literal in the EDN, to a concrete implementation. ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { - impl<'a> Context<'a, $type> for $State { - fn get (&'a $self, atom: &'a impl Atom) -> Option<$type> { + impl Context<$type> for $State { + fn get (&$self, atom: &impl Atom) -> Option<$type> { Some(match (atom.kind(), atom.text()) { $((TokenKind::Sym, $pat) => $expr,)* (TokenKind::Num, _) => atom.num() as $type, @@ -395,12 +405,3 @@ impl<'a, T: Context<'a, U>, U> Context<'a, U> for Option { } } } -#[macro_export] macro_rules! try_delegate { - ($s:ident, $atom:expr, $T:ty) => { - if let [head, tail @ ..] = $atom.as_slice() { - if let Some(value) = <$T>::try_from_atoms($s, head, tail) { - return Some(value.boxed()) - } - } - } -} diff --git a/input/src/keymap.rs b/input/src/keymap.rs index e3523b90..c769cd3a 100644 --- a/input/src/keymap.rs +++ b/input/src/keymap.rs @@ -110,7 +110,7 @@ pub trait AtomCommand: Command { fn from_atom <'a> ( $state: &$T, head: &impl Atom, tail: &'a [impl Atom] ) -> Option { - $(if let (TokenType::Key, $key, [ // if the identifier matches + $(if let (TokenKind::Key, $key, [ // if the identifier matches // bind argument ids $($arg),* // bind rest parameters @@ -153,7 +153,7 @@ pub trait AtomCommand: Command { head: &impl Atom, tail: &'a [impl Atom], ) -> Option { - $(if let (TokenType::Key, $key, [ // if the identifier matches + $(if let (TokenKind::Key, $key, [ // if the identifier matches // bind argument ids $($arg),* // bind rest parameters diff --git a/output/src/op_align.rs b/output/src/op_align.rs index b8598cbd..7ec5264c 100644 --- a/output/src/op_align.rs +++ b/output/src/op_align.rs @@ -5,17 +5,17 @@ try_from_atoms!(<'a, E>: Align>: |state, atoms| { let head = atoms.next()?; if head.kind() != TokenKind::Key { return None } match head.text() { - "align/c" => return Some(Self::c(state.get_content(atoms.next()?).expect("no content"))), - "align/x" => return Some(Self::x(state.get_content(atoms.next()?).expect("no content"))), - "align/y" => return Some(Self::y(state.get_content(atoms.next()?).expect("no content"))), - "align/n" => return Some(Self::n(state.get_content(atoms.next()?).expect("no content"))), - "align/s" => return Some(Self::s(state.get_content(atoms.next()?).expect("no content"))), - "align/e" => return Some(Self::e(state.get_content(atoms.next()?).expect("no content"))), - "align/w" => return Some(Self::w(state.get_content(atoms.next()?).expect("no content"))), - "align/nw" => return Some(Self::nw(state.get_content(atoms.next()?).expect("no content"))), - "align/ne" => return Some(Self::ne(state.get_content(atoms.next()?).expect("no content"))), - "align/sw" => return Some(Self::sw(state.get_content(atoms.next()?).expect("no content"))), - "align/se" => return Some(Self::se(state.get_content(atoms.next()?).expect("no content"))), + "align/c" => return Some(Self::c(state.get_content(&atoms.next()?).expect("no content"))), + "align/x" => return Some(Self::x(state.get_content(&atoms.next()?).expect("no content"))), + "align/y" => return Some(Self::y(state.get_content(&atoms.next()?).expect("no content"))), + "align/n" => return Some(Self::n(state.get_content(&atoms.next()?).expect("no content"))), + "align/s" => return Some(Self::s(state.get_content(&atoms.next()?).expect("no content"))), + "align/e" => return Some(Self::e(state.get_content(&atoms.next()?).expect("no content"))), + "align/w" => return Some(Self::w(state.get_content(&atoms.next()?).expect("no content"))), + "align/nw" => return Some(Self::nw(state.get_content(&atoms.next()?).expect("no content"))), + "align/ne" => return Some(Self::ne(state.get_content(&atoms.next()?).expect("no content"))), + "align/sw" => return Some(Self::sw(state.get_content(&atoms.next()?).expect("no content"))), + "align/se" => return Some(Self::se(state.get_content(&atoms.next()?).expect("no content"))), _ => {} } }); diff --git a/output/src/op_bsp.rs b/output/src/op_bsp.rs index 54f5f650..45d40061 100644 --- a/output/src/op_bsp.rs +++ b/output/src/op_bsp.rs @@ -1,5 +1,4 @@ use crate::*; -use RefAtom::*; pub use self::Direction::*; /// A cardinal direction. #[derive(Copy, Clone, PartialEq)] @@ -37,28 +36,28 @@ try_from_atoms!(<'a, E>: Bsp, RenderBox<'a, E>>: |state, atoms| if head.kind() != TokenKind::Key { return None } match head.text() { "bsp/n" => return Some(Self::n( - state.get_content(atoms.next()?).expect("no south"), - state.get_content(atoms.next()?).expect("no north") + state.get_content(&atoms.next()?).expect("no south"), + state.get_content(&atoms.next()?).expect("no north") )), "bsp/s" => return Some(Self::s( - state.get_content(atoms.next()?).expect("no north"), - state.get_content(atoms.next()?).expect("no south") + state.get_content(&atoms.next()?).expect("no north"), + state.get_content(&atoms.next()?).expect("no south") )), "bsp/e" => return Some(Self::e( - state.get_content(atoms.next()?).expect("no west"), - state.get_content(atoms.next()?).expect("no east") + state.get_content(&atoms.next()?).expect("no west"), + state.get_content(&atoms.next()?).expect("no east") )), "bsp/w" => return Some(Self::w( - state.get_content(atoms.next()?).expect("no east"), - state.get_content(atoms.next()?).expect("no west") + state.get_content(&atoms.next()?).expect("no east"), + state.get_content(&atoms.next()?).expect("no west") )), "bsp/a" => return Some(Self::a( - state.get_content(atoms.next()?).expect("no above"), - state.get_content(atoms.next()?).expect("no below") + state.get_content(&atoms.next()?).expect("no above"), + state.get_content(&atoms.next()?).expect("no below") )), "bsp/b" => return Some(Self::b( - state.get_content(atoms.next()?).expect("no above"), - state.get_content(atoms.next()?).expect("no below") + state.get_content(&atoms.next()?).expect("no above"), + state.get_content(&atoms.next()?).expect("no below") )), _ => {} } @@ -124,22 +123,18 @@ impl, B: Content> BspAreas for Bsp { fn direction (&self) -> Direction { self.0 } fn contents (&self) -> (&A, &B) { (&self.1, &self.2) } } - /// Renders multiple things on top of each other, #[macro_export] macro_rules! lay { ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::b(bsp, $expr);)*; bsp }} } - /// Stack southward. #[macro_export] macro_rules! col { ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::s(bsp, $expr);)*; bsp }}; } - /// Stack northward. #[macro_export] macro_rules! col_up { ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::n(bsp, $expr);)*; bsp }} } - /// Stack eastward. #[macro_export] macro_rules! row { ($($expr:expr),* $(,)?) => {{ let bsp = (); $(let bsp = Bsp::e(bsp, $expr);)*; bsp }}; diff --git a/output/src/op_cond.rs b/output/src/op_cond.rs index 77fbe549..48757565 100644 --- a/output/src/op_cond.rs +++ b/output/src/op_cond.rs @@ -9,9 +9,9 @@ try_from_atoms!(<'a, E>: When>: |state, atoms| { let head = atoms.next()?; if (head.kind(), head.text()) == (TokenKind::Key, "when") { let condition = atoms.next(); - if let Some(condition) = condition { + if let Some(ref condition) = condition { let condition = state.get_bool(condition).expect("no condition"); - if let Some(content) = atoms.next() { + if let Some(ref content) = atoms.next() { let content = state.get_content(content).expect("no atom"); return Some(Self(condition, content)) } @@ -22,11 +22,11 @@ try_from_atoms!(<'a, E>: Either, RenderBox<'a, E>>: |state, ato let head = atoms.next()?; if (head.kind(), head.text()) == (TokenKind::Key, "either") { let condition = atoms.next(); - if let Some(condition) = condition { + if let Some(ref condition) = condition { let condition = state.get_bool(condition).expect("no condition"); - if let Some(content1) = atoms.next() { + if let Some(ref content1) = atoms.next() { let content1 = state.get_content(content1).expect("no content1"); - if let Some(content2) = atoms.next() { + if let Some(ref content2) = atoms.next() { let content2 = state.get_content(content2).expect("no content2"); return Some(Self(condition, content1, content2)) } diff --git a/output/src/op_transform.rs b/output/src/op_transform.rs index 4fea2486..7b31ccec 100644 --- a/output/src/op_transform.rs +++ b/output/src/op_transform.rs @@ -13,6 +13,24 @@ macro_rules! transform_xy { pub fn y (item: T) -> Self { Self::Y(item) } pub fn xy (item: T) -> Self { Self::XY(item) } } + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { + fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { + let head = atoms.next()?; + if head.kind() != TokenKind::Key { return None } + Some(match head.text() { + $x => Self::x( + state.get_content(&atoms.next().expect("no content")).expect("no content") + ), + $y => Self::y( + state.get_content(&atoms.next().expect("no content")).expect("no content") + ), + $xy => Self::xy( + state.get_content(&atoms.next().expect("no content")).expect("no content") + ), + _ => return None + }) + } + } impl> Content for $Enum { fn content (&self) -> impl Render { match self { @@ -26,24 +44,6 @@ macro_rules! transform_xy { $area } } - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { - fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - Some(match head.text() { - $x => Self::x( - state.get_content(atoms.next().expect("no content")).expect("no content") - ), - $y => Self::y( - state.get_content(atoms.next().expect("no content")).expect("no content") - ), - $xy => Self::xy( - state.get_content(atoms.next().expect("no content")).expect("no content") - ), - _ => return None - }) - } - } } } /// Defines an enum that parametrically transforms its content @@ -56,16 +56,26 @@ macro_rules! transform_xy_unit { pub fn y (y: U, item: T) -> Self { Self::Y(y, item) } pub fn xy (x: U, y: U, item: T) -> Self { Self::XY(x, y, item) } } - impl $Enum { - pub fn dx (&self) -> U { - match self { - Self::X(x, _) => *x, Self::Y(_, _) => 0.into(), Self::XY(x, _, _) => *x, - } - } - pub fn dy (&self) -> U { - match self { - Self::X(_, _) => 0.into(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y, - } + impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { + fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { + let head = atoms.next()?; + if head.kind() != TokenKind::Key { return None } + Some(match head.text() { + $x => Self::x( + state.get_unit(&atoms.next().expect("no x")).expect("no x"), + state.get_content(&atoms.next().expect("no content")).expect("no content") + ), + $y => Self::y( + state.get_unit(&atoms.next().expect("no y")).expect("no y"), + state.get_content(&atoms.next().expect("no content")).expect("no content") + ), + $xy => Self::xy( + state.get_unit(&atoms.next().expect("no x")).expect("no x"), + state.get_unit(&atoms.next().expect("no y")).expect("no y"), + state.get_content(&atoms.next().expect("no content")).expect("no content"), + ), + _ => return None + }) } } impl> Content for $Enum { @@ -80,26 +90,16 @@ macro_rules! transform_xy_unit { $layout.into() } } - impl<'a, E: Output + 'a, T: ViewContext<'a, E>> TryFromAtom<'a, T> for $Enum> { - fn try_from_atoms (state: &'a T, mut atoms: impl Iterator> + 'a) -> Option { - let head = atoms.next()?; - if head.kind() != TokenKind::Key { return None } - Some(match head.text() { - $x => Self::x( - state.get_unit(atoms.next().expect("no x")).expect("no x"), - state.get_content(atoms.next().expect("no content")).expect("no content") - ), - $y => Self::y( - state.get_unit(atoms.next().expect("no y")).expect("no y"), - state.get_content(atoms.next().expect("no content")).expect("no content") - ), - $xy => Self::xy( - state.get_unit(atoms.next().expect("no x")).expect("no x"), - state.get_unit(atoms.next().expect("no y")).expect("no y"), - state.get_content(atoms.next().expect("no content")).expect("no content"), - ), - _ => return None - }) + impl $Enum { + pub fn dx (&self) -> U { + match self { + Self::X(x, _) => *x, Self::Y(_, _) => 0.into(), Self::XY(x, _, _) => *x, + } + } + pub fn dy (&self) -> U { + match self { + Self::X(_, _) => 0.into(), Self::Y(y, _) => *y, Self::XY(_, y, _) => *y, + } } } } @@ -111,7 +111,6 @@ transform_xy!("fill/x" "fill/y" "fill/xy" |self: Fill, to|{ X(_) => [x0, y, wmax, h], Y(_) => [x, y0, w, hmax], XY(_) => [x0, y0, wmax, hmax], - _ => unreachable!() }.into() }); transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ @@ -120,14 +119,12 @@ transform_xy_unit!("fixed/x" "fixed/y" "fixed/xy"|self: Fixed, area|{ Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], - _ => unreachable!(), }; let [x, y, w, h] = Render::layout(&self.content(), fixed_area.into()).xywh(); let fixed_area = match self { Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], - _ => unreachable!(), }; fixed_area }); @@ -137,7 +134,6 @@ transform_xy_unit!("min/x" "min/y" "min/xy"|self: Min, area|{ Self::X(mw, _) => [area.x(), area.y(), area.w().max(*mw), area.h()], Self::Y(mh, _) => [area.x(), area.y(), area.w(), area.h().max(*mh)], Self::XY(mw, mh, _) => [area.x(), area.y(), area.w().max(*mw), area.h().max(*mh)], - _ => unreachable!(), }}); transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{ let [x, y, w, h] = area.xywh(); @@ -145,15 +141,13 @@ transform_xy_unit!("max/x" "max/y" "max/xy"|self: Max, area|{ Self::X(fw, _) => [x, y, *fw, h], Self::Y(fh, _) => [x, y, w, *fh], Self::XY(fw, fh, _) => [x, y, *fw, *fh], - _ => unreachable!(), }.into())}); - -transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|Render::layout(&self.content(), [ - area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy()) -].into())); -transform_xy_unit!("expand/x" "expand/y" "expand/xy"|self: Expand, area|Render::layout(&self.content(), [ - area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy() -].into())); +transform_xy_unit!("shrink/x" "shrink/y" "shrink/xy"|self: Shrink, area|Render::layout( + &self.content(), + [area.x(), area.y(), area.w().minus(self.dx()), area.h().minus(self.dy())].into())); +transform_xy_unit!("expand/x" "expand/y" "expand/xy"|self: Expand, area|Render::layout( + &self.content(), + [area.x(), area.y(), area.w() + self.dx(), area.h() + self.dy()].into())); transform_xy_unit!("push/x" "push/y" "push/xy"|self: Push, area|{ let area = Render::layout(&self.content(), area); [area.x() + self.dx(), area.y() + self.dy(), area.w(), area.h()] diff --git a/output/src/view.rs b/output/src/view.rs index 8ab4271f..7a27dc55 100644 --- a/output/src/view.rs +++ b/output/src/view.rs @@ -1,5 +1,5 @@ use crate::*; -use std::{sync::Arc, marker::PhantomData}; +use std::{sync::Arc, fmt::Debug}; use TokenKind::*; /// Define an EDN-backed view. /// @@ -46,104 +46,112 @@ use TokenKind::*; } } /// Renders from EDN source and context. -#[derive(Default)] pub enum AtomView<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> { +/// +/// Generic over: +/// +/// * `O` - Output target. +/// * `S` - State provider. +/// * `A` - Atom storage type. +#[derive(Default, Clone)] pub enum AtomView<'a, O, S, A> { + _Unused(std::marker::PhantomData<&'a O>), #[default] Inert, - Ok(T, Atom>), - //render: BoxBox + Send + Sync + 'a> + Send + Sync + 'a> - Err(String), - _Unused(PhantomData<&'a E>), + Src(S, &'a str), + Ref(S, &'a A), + Own(S, A), + Err(Arc) } -impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> std::fmt::Debug for AtomView<'a, E, T> { +impl<'a, O, S, A> Debug for AtomView<'a, O, S, A> +where S: Debug, A: Debug { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { - Self::Inert | Self::_Unused(_) => + Self::Inert => write!(f, "AtomView::Inert"), - Self::Ok(state, view) => - write!(f, "AtomView::Ok(state={state:?} view={view:?}"), + Self::Src(state, view) => + write!(f, "AtomView::Src(state={state:?} view={view:?}"), + Self::Ref(state, view) => + write!(f, "AtomView::Ref(state={state:?} view={view:?}"), + Self::Own(state, view) => + write!(f, "AtomView::Arc(state={state:?} view={view:?}"), Self::Err(error) => write!(f, "AtomView::Err({error})"), _ => unreachable!() } } } -impl<'a, E: Output, T> ViewContext<'a, E> for T where T: - Context<'a, bool> + - Context<'a, usize> + - Context<'a, E::Unit> + - Context<'a, Box + 'a>> -{} -impl<'a, E: Output, T: ViewContext<'a, E> + std::fmt::Debug> AtomView<'a, E, T> { - pub fn from_source (state: T, source: &'a str) -> Self { - match RefAtom::read_one(&source) { - Ok((layout, _)) => Self::Ok(state, layout), - Err(error) => Self::Err(format!("{error} in {source}")) - } - } - pub fn from_atoms (state: T, atoms: Vec) -> Self { - Self::Ok(state, Atom::Exp(atoms).to_arc()) - } -} -implViewContext<'a, E> + Send + Sync + std::fmt::Debug> Content for AtomView<'_, E, T> { - fn content (&self) -> impl Render { +impl<'a, O, S, A> Content for AtomView<'a, O, S, A> +where O: Output, S: ViewContext<'a, O>, A: Atom { + fn content (&self) -> impl Render { match self { - Self::Ok(state, layout) => state.get_content(layout), - Self::Err(_error) => { - panic!("{self:?}"); - //TODO:&format!("AtomView error: {error:?}")) // FIXME: String is not Render - }, - _ => todo!() + Self::Inert => { panic!("inert rendered") }, + Self::Err(e) => { panic!("render error: {e}") }, + Self::Src(state, source) => {}, + Self::Ref(state, atom) => {}, + Self::Own(state, atom) => {}, + _ => unreachable!() } } } +impl<'a, E: Output, T> ViewContext<'a, E> for T where T: Sized + Send + Sync + + Context + + Context + + Context + + Context + 'a>> {} /// Provides values to the template -pub trait ViewContext<'a, E: Output>: - Context<'a, bool> + - Context<'a, usize> + - Context<'a, E::Unit> + - Context<'a, Box + 'a>> +pub trait ViewContext<'a, E: Output>: Sized + Send + Sync + + Context + + Context + + Context + + Context + 'a>> { - fn get_bool (&'a self, atom: RefAtom<'a>) -> Option { + fn get_bool (&self, atom: &impl Atom) -> Option { Some(match atom.kind() { - Sym => match atom.text().as_ref() { - ":false" | ":f" => false, - ":true" | ":t" => true, + Num => match atom.num() { 0 => false, _ => true }, + Sym => match atom.text() { + ":false" | ":f" => false, ":true" | ":t" => true, _ => return Context::get(self, atom) }, - Num => match atom.num() { - 0 => false, - _ => true - }, _ => return Context::get(self, atom) }) } - fn get_usize (&'a self, atom: RefAtom<'a>) -> Option { + fn get_usize (&self, atom: &impl Atom) -> Option { Some(match atom.kind() { Num => atom.num(), _ => return Context::get(self, atom) }) } - fn get_unit (&'a self, atom: RefAtom<'a>) -> Option { + fn get_unit (&self, atom: &impl Atom) -> Option { Some(match atom.kind() { Num => E::Unit::from(atom.num() as u16), _ => return Context::get(self, atom) }) } - fn get_content (&'a self, atom: RefAtom<'a>) -> Option + 'a>> where E: 'a { - try_delegate!(self, atom, When::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Either::<_, RenderBox<'a, E>, RenderBox<'a, E>>); - try_delegate!(self, atom, Align::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Bsp::<_, RenderBox<'a, E>, RenderBox<'a, E>>); - try_delegate!(self, atom, Fill::<_, RenderBox<'a, E>>); - try_delegate!(self, atom, Fixed::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Min::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Max::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Shrink::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Expand::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Push::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Pull::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Margin::<_, _, RenderBox<'a, E>>); - try_delegate!(self, atom, Padding::<_, _, RenderBox<'a, E>>); - Context::get_or_fail(self, atom) + fn get_content (&self, atom: &impl Atom) -> Option + 'a>> where E: 'a { + try_delegate!(self, atom, When::>); + try_delegate!(self, atom, Either::, RenderBox<'a, E>>); + try_delegate!(self, atom, Align::>); + try_delegate!(self, atom, Bsp::, RenderBox<'a, E>>); + try_delegate!(self, atom, Fill::>); + try_delegate!(self, atom, Fixed::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Min::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Max::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Shrink::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Expand::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Push::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Pull::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Margin::<_, RenderBox<'a, E>>); + try_delegate!(self, atom, Padding::<_, RenderBox<'a, E>>); + Some(Context::get_or_fail(self, atom)) + } +} +#[macro_export] macro_rules! try_delegate { + ($s:ident, $atom:expr, $T:ty) => { + if $atom.kind() == TokenKind::Exp { + if let [head, tail @ ..] = $atom.as_slice() { + if let Some(value) = <$T>::try_from_atoms($s, head, tail) { + return Some(value.boxed()) + } + } + } } } // A function that returns a `RenderBox.