diff --git a/src/cli_v2/mod.rs b/src/cli_v2/mod.rs new file mode 100644 index 0000000..a960c5a --- /dev/null +++ b/src/cli_v2/mod.rs @@ -0,0 +1 @@ +pub(crate) mod value_vec; diff --git a/src/cli_v2/value_vec.rs b/src/cli_v2/value_vec.rs new file mode 100644 index 0000000..d96c9c3 --- /dev/null +++ b/src/cli_v2/value_vec.rs @@ -0,0 +1,84 @@ +use std::sync::{Arc, RwLock}; + +use minijinja::{ErrorKind, value::DynObject}; + +pub(crate) fn new_value_vec() -> DynObject { + DynObject::new(Arc::new(ValueVec(RwLock::new(vec![])))) +} + +impl minijinja::value::Object for ValueVec { + fn repr(self: &Arc) -> minijinja::value::ObjectRepr { + minijinja::value::ObjectRepr::Iterable + } + + fn call_method( + self: &Arc, + _state: &minijinja::State<'_, '_>, + method: &str, + args: &[minijinja::Value], + ) -> Result { + match method { + "push" => self.push(args), + _ => Err(minijinja::Error::new( + ErrorKind::UnknownMethod, + format!("Unexpected method {method}"), + )), + } + } + + fn enumerate(self: &Arc) -> minijinja::value::Enumerator { + let vals = self + .0 + .read() + .expect("Unable to read from ValueVec, RwLock was poisoned") + .iter() + .map(|s| s.to_owned()) + .collect::>(); + minijinja::value::Enumerator::Iter(Box::new(vals.into_iter())) + } +} + +/// List of `minijinja::Value`, a workaround for `minijinja`s mutability limitations. +/// +/// Like `minijinja`s own `namespace`, but in array form instead of dict form. +#[derive(Debug)] +struct ValueVec(RwLock>); + +impl ValueVec { + fn push( + self: &Arc, + args: &[minijinja::Value], + ) -> Result { + ensure_n_args("push", 1, args)?; + { + let mut list = self + .0 + .try_write() + .map_err(|e| minijinja::Error::new(ErrorKind::InvalidOperation, e.to_string()))?; + list.push(args[0].clone()); + } + Ok(minijinja::Value::UNDEFINED) + } +} + +fn ensure_n_args( + method: &str, + n: usize, + args: &[minijinja::Value], +) -> Result<(), minijinja::Error> { + let err = |kind| -> Result<(), minijinja::Error> { + Err(minijinja::Error::new( + kind, + format!( + "{method} | Expected: {n} args, got {} arguments", + args.len() + ), + )) + }; + + match args.len().cmp(&n) { + std::cmp::Ordering::Less => err(ErrorKind::MissingArgument), + std::cmp::Ordering::Greater => err(ErrorKind::TooManyArguments), + std::cmp::Ordering::Equal => Ok(()), + } +} diff --git a/src/lib.rs b/src/lib.rs index 25e2db0..bb0874a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ mod api; mod cli_v1; +pub(crate) mod cli_v2; mod codesamples; mod generator; mod postprocessing; diff --git a/src/template.rs b/src/template.rs index af8ba2f..9e14cd1 100644 --- a/src/template.rs +++ b/src/template.rs @@ -21,6 +21,9 @@ pub fn env_with_dir( pub fn populate_env( mut env: minijinja::Environment<'static>, ) -> Result, minijinja::Error> { + // === Custom functions === + env.add_function("vec", crate::cli_v2::value_vec::new_value_vec); + // === Custom filters === // --- Case conversion ---