use core::fmt::Write; pub mod internal; use internal::{ execute::execute, parse::{parse, Ast}, }; pub trait Value { fn print(&self, out: &mut String); fn lookup(&self, name: &str) -> Option<&dyn Value>; fn has(&self) -> Option<&dyn Value>; fn if_(&self) -> bool; fn for_(&self, index: usize) -> Option<&dyn Value>; } macro_rules! value_for_int { ($tp:ty) => { impl Value for $tp { fn print(&self, out: &mut String) { let _ = write!(out, "{self}"); } fn lookup(&self, _: &str) -> Option<&dyn Value> { None } fn has(&self) -> Option<&dyn Value> { if self.if_() { Some(self) } else { None } } fn if_(&self) -> bool { *self != 0 } fn for_(&self, _: usize) -> Option<&dyn Value> { None } } }; } value_for_int!(u8); value_for_int!(u16); value_for_int!(u32); value_for_int!(u64); value_for_int!(u128); value_for_int!(usize); value_for_int!(i8); value_for_int!(i16); value_for_int!(i32); value_for_int!(i64); value_for_int!(i128); value_for_int!(isize); impl<'a, T> Value for &'a T where T: Value + ?Sized, { fn print(&self, out: &mut String) { (&**self).print(out) } fn lookup(&self, name: &str) -> Option<&dyn Value> { (&**self).lookup(name) } fn has(&self) -> Option<&dyn Value> { (&**self).has() } fn if_(&self) -> bool { (&**self).if_() } fn for_(&self, index: usize) -> Option<&dyn Value> { (&**self).for_(index) } } impl<'a, T> Value for &'a mut T where T: Value + ?Sized, { fn print(&self, out: &mut String) { (&**self).print(out) } fn lookup(&self, name: &str) -> Option<&dyn Value> { (&**self).lookup(name) } fn has(&self) -> Option<&dyn Value> { (&**self).has() } fn if_(&self) -> bool { (&**self).if_() } fn for_(&self, index: usize) -> Option<&dyn Value> { (&**self).for_(index) } } impl Value for &str { fn print(&self, out: &mut String) { out.push_str(self); } fn lookup(&self, _: &str) -> Option<&dyn Value> { None } fn has(&self) -> Option<&dyn Value> { if self.if_() { None } else { Some(self) } } fn if_(&self) -> bool { !self.is_empty() } fn for_(&self, index: usize) -> Option<&dyn Value> { if index == 0 { Some(self) } else { None } } } impl Value for String { fn print(&self, out: &mut String) { out.push_str(self); } fn lookup(&self, _: &str) -> Option<&dyn Value> { None } fn has(&self) -> Option<&dyn Value> { if self.if_() { None } else { Some(self) } } fn if_(&self) -> bool { !self.is_empty() } fn for_(&self, index: usize) -> Option<&dyn Value> { if index == 0 { Some(self) } else { None } } } impl Value for (A, B) where A: Value, B: Value, { fn print(&self, out: &mut String) { self.0.print(out); self.1.print(out); } fn lookup(&self, name: &str) -> Option<&dyn Value> { match name { "0" => Some(&self.0), "1" => Some(&self.1), _ => None, } } fn has(&self) -> Option<&dyn Value> { None } fn if_(&self) -> bool { self.0.if_() && self.1.if_() } fn for_(&self, index: usize) -> Option<&dyn Value> { match index { 0 => Some(&self.0), 1 => Some(&self.1), _ => None, } } } pub struct ValuesListMap<'a>(pub &'a [(&'a str, &'a dyn Value)]); impl<'a> Value for ValuesListMap<'a> { fn print(&self, w: &mut String) { for (name, v) in self.0.iter() { let _ = write!(w, "{name}"); v.print(w); } } fn lookup(&self, name: &str) -> Option<&dyn Value> { self.0.iter().find(|(n, _)| *n == name).map(|(_, v)| *v) } fn has(&self) -> Option<&dyn Value> { if self.0.is_empty() { None } else { Some(self) } } fn if_(&self) -> bool { !self.0.is_empty() } fn for_(&self, index: usize) -> Option<&dyn Value> { self.0.get(index).map(|v| v as &dyn Value) } } impl Value for [T; N] where T: Value, { fn print(&self, w: &mut String) { for v in self.iter() { v.print(w) } } fn lookup(&self, name: &str) -> Option<&dyn Value> { let idx: usize = name.parse().ok()?; self.get(idx).map(|v| v as &dyn Value) } fn has(&self) -> Option<&dyn Value> { if self.is_empty() { None } else { Some(self) } } fn if_(&self) -> bool { self.is_empty() } fn for_(&self, index: usize) -> Option<&dyn Value> { self.get(index).map(|v| v as &dyn Value) } } impl Value for Option where T: Value, { fn print(&self, out: &mut String) { if let Some(v) = self { v.print(out) } } fn lookup(&self, _: &str) -> Option<&dyn Value> { None } fn has(&self) -> Option<&dyn Value> { self.as_ref().map(|v| v as &dyn Value) } fn if_(&self) -> bool { self.is_some() } fn for_(&self, index: usize) -> Option<&dyn Value> { if index == 0 { self.has() } else { None } } } impl Value for Vec where T: Value, { fn print(&self, w: &mut String) { for v in self.iter() { v.print(w) } } fn lookup(&self, name: &str) -> Option<&dyn Value> { let idx: usize = name.parse().ok()?; self.get(idx).map(|v| v as &dyn Value) } fn has(&self) -> Option<&dyn Value> { if self.is_empty() { None } else { Some(self) } } fn if_(&self) -> bool { self.is_empty() } fn for_(&self, index: usize) -> Option<&dyn Value> { self.get(index).map(|v| v as &dyn Value) } } pub struct Newt { ast: Ast, } impl Newt { pub fn build(tmpl: &str) -> Self { let ast = parse(tmpl); Self { ast: todo!() } } pub fn execute(&self, value: &dyn Value) -> String { execute(&self.ast, value) } } #[macro_export] macro_rules! values_list_map { ($($key:literal: $val:expr),* $(,)?) => {{ &$crate::ValuesListMap( &[ $( ($key, $val as &dyn Value) )*, ] ) }} } #[macro_export] macro_rules! values { ($($val:expr),* $(,)?) => {{ &[ $( &$val as &dyn Value, )* ] }} } #[cfg(test)] mod test { use crate::{Newt, Value}; fn go(tmpl: &str, value: &dyn Value, expected: &str) { assert_eq!(Newt::build(tmpl).execute(value), expected) } #[test] fn simple_print() { go("Hello, {.}!", &"World", "Hello, World!") } #[test] fn print_lookup() { go( "Hello, {.name}!", values_list_map! { "name": &"Ted", }, "Hello, Ted!", ); } #[test] fn print_for() { go( "{for .}{.}, {/for}", values!["Bob", "Larry"], "Bob, Larry, ", ); } #[test] fn print_for_nested() { go( "{for .items}{for .names}{.}, {/for}{/for}", values_list_map! { "items": values![values_list_map! { "names": values!["Bob", "Larry", 0], }] }, "Bob, Larry, 0, ", ) } #[test] fn print_has() { go("{has .}{.}{/has}", &Some("Bob"), "Bob"); } #[test] fn print_has_none() { go("{has .}{.}{/has}", &None::<&str>, ""); } #[test] fn print_if() { go("{if .}{.}{/if}", &"Bob", "Bob"); } #[test] fn print_if_false() { go("{if .}{.}{/if}", &"", ""); } #[test] fn print_if_false_else() { go("{if .}{.}{else}Frank{/if}", &"", "Frank"); } }