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::Null => {} Value::Array(values) => {
36 for value in values {
37 let s = value_to_string(value)?;
38 if !s.is_empty() {
39 out.push((rendered_key.clone(), s));
40 }
41 }
42 }
43 other => {
44 let s = value_to_string(other)?;
45 if !s.is_empty() {
49 out.push((rendered_key, s));
50 }
51 }
52 }
53 }
54
55 Ok(out)
56}
57
58pub fn value_to_string(value: Value) -> Result<String> {
60 let out = match value {
61 Value::Null => String::new(),
62 Value::Bool(v) => v.to_string(),
63 Value::Number(v) => v.to_string(),
64 Value::String(v) => v,
65 Value::Array(_) | Value::Object(_) => serde_json::to_string(&value)?,
66 };
67 Ok(out)
68}