Skip to content

Commit 260154e

Browse files
authored
Add function vec returns a list mutable from the minijinja context (#143)
The `ValueVec` helps with the efforts to emit already formatted code. It's has a simple `push` method, and it implements `enumerate` so I will be able to iterate through the vec from within a template.
1 parent 7a3fadb commit 260154e

File tree

4 files changed

+89
-0
lines changed

4 files changed

+89
-0
lines changed

src/cli_v2/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod value_vec;

src/cli_v2/value_vec.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use std::sync::{Arc, RwLock};
2+
3+
use minijinja::{ErrorKind, value::DynObject};
4+
5+
pub(crate) fn new_value_vec() -> DynObject {
6+
DynObject::new(Arc::new(ValueVec(RwLock::new(vec![]))))
7+
}
8+
9+
impl minijinja::value::Object for ValueVec {
10+
fn repr(self: &Arc<Self>) -> minijinja::value::ObjectRepr {
11+
minijinja::value::ObjectRepr::Iterable
12+
}
13+
14+
fn call_method(
15+
self: &Arc<Self>,
16+
_state: &minijinja::State<'_, '_>,
17+
method: &str,
18+
args: &[minijinja::Value],
19+
) -> Result<minijinja::Value, minijinja::Error> {
20+
match method {
21+
"push" => self.push(args),
22+
_ => Err(minijinja::Error::new(
23+
ErrorKind::UnknownMethod,
24+
format!("Unexpected method {method}"),
25+
)),
26+
}
27+
}
28+
29+
fn enumerate(self: &Arc<Self>) -> minijinja::value::Enumerator {
30+
let vals = self
31+
.0
32+
.read()
33+
.expect("Unable to read from ValueVec, RwLock was poisoned")
34+
.iter()
35+
.map(|s| s.to_owned())
36+
.collect::<Vec<_>>();
37+
minijinja::value::Enumerator::Iter(Box::new(vals.into_iter()))
38+
}
39+
}
40+
41+
/// List of `minijinja::Value`, a workaround for `minijinja`s mutability limitations.
42+
///
43+
/// Like `minijinja`s own `namespace`, but in array form instead of dict form.
44+
#[derive(Debug)]
45+
struct ValueVec(RwLock<Vec<minijinja::Value>>);
46+
47+
impl ValueVec {
48+
fn push(
49+
self: &Arc<Self>,
50+
args: &[minijinja::Value],
51+
) -> Result<minijinja::Value, minijinja::Error> {
52+
ensure_n_args("push", 1, args)?;
53+
{
54+
let mut list = self
55+
.0
56+
.try_write()
57+
.map_err(|e| minijinja::Error::new(ErrorKind::InvalidOperation, e.to_string()))?;
58+
list.push(args[0].clone());
59+
}
60+
Ok(minijinja::Value::UNDEFINED)
61+
}
62+
}
63+
64+
fn ensure_n_args(
65+
method: &str,
66+
n: usize,
67+
args: &[minijinja::Value],
68+
) -> Result<(), minijinja::Error> {
69+
let err = |kind| -> Result<(), minijinja::Error> {
70+
Err(minijinja::Error::new(
71+
kind,
72+
format!(
73+
"{method} | Expected: {n} args, got {} arguments",
74+
args.len()
75+
),
76+
))
77+
};
78+
79+
match args.len().cmp(&n) {
80+
std::cmp::Ordering::Less => err(ErrorKind::MissingArgument),
81+
std::cmp::Ordering::Greater => err(ErrorKind::TooManyArguments),
82+
std::cmp::Ordering::Equal => Ok(()),
83+
}
84+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod api;
22
mod cli_v1;
3+
pub(crate) mod cli_v2;
34
mod codesamples;
45
mod generator;
56
mod postprocessing;

src/template.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ pub fn env_with_dir(
2121
pub fn populate_env(
2222
mut env: minijinja::Environment<'static>,
2323
) -> Result<minijinja::Environment<'static>, minijinja::Error> {
24+
// === Custom functions ===
25+
env.add_function("vec", crate::cli_v2::value_vec::new_value_vec);
26+
2427
// === Custom filters ===
2528

2629
// --- Case conversion ---

0 commit comments

Comments
 (0)