use crate::*; /// Map EDN tokens to parameters of a given type for a given context pub trait Context: Sized { fn get (&self, _atom: &impl Atom) -> Option { None } fn get_or_fail (&self, atom: &impl Atom) -> U { self.get(atom).expect("no value") } } impl, U> Context for &T { fn get (&self, atom: &impl Atom) -> Option { (*self).get(atom) } fn get_or_fail (&self, atom: &impl Atom) -> U { (*self).get_or_fail(atom) } } 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 (&self, atom: &impl Atom) -> U { self.as_ref().map(|s|s.get_or_fail(atom)).expect("no provider") } } /// Implement `Context` for a context and type. #[macro_export] macro_rules! provide { // Provide a value to the EDN template ($type:ty:|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { 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 }) } } }; // 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: &impl Atom) -> Option<$type> { Some(match (atom.kind(), atom.text()) { $((TokenKind::Sym, $pat) => $expr,)* _ => return None }) } } }; } /// Implement `Context` for a context and numeric type. /// /// This enables support for numeric literals. #[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<$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, _ => return None }) } } }; // 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 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, _ => return None }) } } }; } /// Implement `Context` for a context and content type. /// /// This enables support for layout expressions. #[macro_export] macro_rules! provide_content { (|$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<'a, E: Output> Context<'a, Box + 'a>> for $State { fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { use RefAtom::*; Some(match atom.to_ref() { $(RefAtom::Sym($pat) => $expr),*, _ => return None }) } } }; ($Output:ty: |$self:ident:$State:ty|{ $($pat:pat => $expr:expr),* $(,)? }) => { impl<'a> Context<'a, Box + 'a>> for $State { fn get (&'a $self, atom: &'a impl Atom) -> Option + 'a>> { use RefAtom::*; Some(match atom.to_ref() { $(Sym($pat) => $expr),*, _ => return None }) } } } }