use crate::*; pub trait TryFromAtom<'a, T>: Sized { fn try_from_expr (_state: &'a T, _iter: TokenIter<'a>) -> Option { None } fn try_from_atom (state: &'a T, value: Value<'a>) -> Option { if let Exp(0, iter) = value { return Self::try_from_expr(state, iter) } None } } pub trait TryIntoAtom: Sized { fn try_into_atom (&self) -> Option; } /// Map EDN tokens to parameters of a given type for a given context pub trait Context: Sized { fn get (&self, _atom: &Value) -> Option { None } fn get_or_fail (&self, atom: &Value) -> U { self.get(atom).expect("no value") } } impl, U> Context for &T { fn get (&self, atom: &Value) -> Option { (*self).get(atom) } fn get_or_fail (&self, atom: &Value) -> U { (*self).get_or_fail(atom) } } impl, U> Context for Option { fn get (&self, atom: &Value) -> Option { self.as_ref().map(|s|s.get(atom)).flatten() } fn get_or_fail (&self, atom: &Value) -> 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 { #[allow(unreachable_code)] fn get (&$self, atom: &Value) -> Option<$type> { use Value::*; Some(match atom { $(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 { #[allow(unreachable_code)] fn get (&$lt $self, atom: &Value) -> Option<$type> { use Value::*; Some(match atom { $(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: &Value) -> Option<$type> { use Value::*; Some(match atom { $(Sym($pat) => $expr,)* Num(n) => *n 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: &Value) -> Option<$type> { use Value::*; Some(match atom { $(Sym($pat) => $expr,)* Num(n) => *n as $type, _ => return None }) } } }; } /// Implement `Context` for a context and the boolean type. /// /// This enables support for boolean literals. #[macro_export] macro_rules! provide_bool { // 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: &Value) -> Option<$type> { use Value::*; Some(match atom { Num(n) => match *n { 0 => false, _ => true }, Sym(":false") | Sym(":f") => false, Sym(":true") | Sym(":t") => true, $(Sym($pat) => $expr,)* _ => return Context::get(self, atom) }) } } }; // 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: &Value) -> Option<$type> { use Value::*; Some(match atom { Num(n) => match *n { 0 => false, _ => true }, Sym(":false") | Sym(":f") => false, Sym(":true") | Sym(":t") => true, $(Sym($pat) => $expr,)* _ => return None }) } } }; }