use dashmap::DashMap;
#[cfg(feature = "math")]
use paste::paste;
use std::borrow::Cow;
use std::sync::Arc;
use crate::error::Error;
use crate::parser::parser;
use crate::render::nodes;
use crate::{builtin, Variable};
#[cfg(feature = "math")]
use crate::gen_math_use;
use self::function::FuncResult;
pub mod function;
pub mod validations;
pub type Function = fn(&[String]) -> FuncResult;
#[allow(clippy::module_name_repetitions)]
#[derive(Clone)]
pub struct SrTemplate<'a> {
delimiter_start: Cow<'a, str>,
delimiter_close: Cow<'a, str>,
variables: Arc<DashMap<Cow<'a, str>, String>>,
functions: Arc<DashMap<Cow<'a, str>, Box<Function>>>,
}
impl<'a> SrTemplate<'a> {
pub fn with_delimiter<U: Into<Cow<'a, str>>>(start: U, close: U) -> Self {
Self {
delimiter_start: start.into(),
delimiter_close: close.into(),
..Default::default()
}
}
pub fn add<V: Variable<'a>>(&self, value: V) {
value.variables().for_each(|(name, value)| {
self.add_variable(name.clone(), value.clone());
});
}
pub fn add_variable<U: Into<Cow<'a, str>>, T: ToString>(&self, name: U, value: T) {
let value = value.to_string();
self.variables
.entry(name.into())
.and_modify(|old| *old = value.clone())
.or_insert_with(|| value.clone());
}
pub fn add_variables<U: Into<Cow<'a, str>>, V: Iterator<Item = (U, &'a dyn ToString)>>(
&self,
values: V,
) {
values.for_each(|(name, value)| {
self.add_variable(name, value.to_string());
});
}
pub fn add_function<T: Into<Cow<'a, str>>>(&self, name: T, func: Function) {
self.functions
.entry(name.into())
.and_modify(|old| *old = Box::new(func.clone()))
.or_insert_with(|| Box::new(func));
}
pub fn add_functions<U: Into<Cow<'a, str>>, V: Iterator<Item = (U, Function)>>(
&self,
values: V,
) {
values.for_each(|(name, func)| {
self.add_function(name, func);
});
}
pub fn contains_variable<T: Into<Cow<'a, str>>>(&self, name: T) -> bool {
self.variables.contains_key(&name.into())
}
pub fn contains_function<T: Into<Cow<'a, str>>>(&self, name: T) -> bool {
self.functions.contains_key(&name.into())
}
pub fn remove_variable<T: Into<Cow<'a, str>>>(&self, name: T) {
self.variables.remove(&name.into());
}
pub fn remove_function<T: Into<Cow<'a, str>>>(&self, name: T) {
self.functions.remove(&name.into());
}
pub fn clear_variables(&self) {
self.variables.clear();
}
pub fn clear_functions(&self) {
self.functions.clear();
}
pub fn set_delimiter<U: Into<Cow<'a, str>>>(&mut self, start: U, close: U) {
self.delimiter_start = start.into();
self.delimiter_close = close.into();
}
pub fn render<T: AsRef<str>>(&self, text: T) -> Result<String, Error> {
let input = text.as_ref();
let open_delim = self.delimiter_start.as_ref();
let close_delim = self.delimiter_close.as_ref();
let mut res = String::with_capacity(input.len());
let tnodes = parser(input, open_delim, close_delim)?;
for var in tnodes {
nodes(
&mut res,
var,
self.variables.as_ref(),
self.functions.as_ref(),
)?;
}
Ok(res)
}
}
impl Default for SrTemplate<'_> {
fn default() -> Self {
let tmp = Self {
delimiter_start: "{{".into(),
delimiter_close: "}}".into(),
variables: Arc::default(),
functions: Arc::default(),
};
#[cfg(feature = "os")]
tmp.add_function("env", builtin::os::env);
#[cfg(feature = "text")]
{
tmp.add_function("toLower", builtin::text::to_lower);
tmp.add_function("toUpper", builtin::text::to_upper);
tmp.add_function("trim", builtin::text::trim);
}
#[cfg(feature = "math")]
{
gen_math_use!(tmp);
}
tmp
}
}