1use std::collections::BTreeMap;
2
3use anyhow::Result;
4use serde_json::Value;
5
6pub trait TemplateRenderer {
9 fn render_str(&self, template: &str, context: &Value) -> Result<String>;
11
12 fn render_value(&self, value: &Value, context: &Value) -> Result<Value>;
14}
15
16pub fn render_key_value_map(
20 input: Option<&BTreeMap<String, Value>>,
21 context: &Value,
22 renderer: &dyn TemplateRenderer,
23) -> Result<Vec<(String, String)>> {
24 let mut out = Vec::new();
25 let Some(input) = input else {
26 return Ok(out);
27 };
28
29 for (key, value) in input {
30 let rendered_key = renderer.render_str(key, context)?;
31 let rendered_value = renderer.render_value(value, context)?;
32
33 match rendered_value {
34 Value::Array(values) => {
35 for value in values {
36 out.push((rendered_key.clone(), value_to_string(value)?));
37 }
38 }
39 other => out.push((rendered_key, value_to_string(other)?)),
40 }
41 }
42
43 Ok(out)
44}
45
46pub fn value_to_string(value: Value) -> Result<String> {
48 let out = match value {
49 Value::Null => String::new(),
50 Value::Bool(v) => v.to_string(),
51 Value::Number(v) => v.to_string(),
52 Value::String(v) => v,
53 Value::Array(_) | Value::Object(_) => serde_json::to_string(&value)?,
54 };
55 Ok(out)
56}