1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
use teller_providers::config::KV;
use tera::{from_value, to_value, Context, Result, Tera};

struct KeyFn {
    kvs: Vec<KV>,
}
impl tera::Function for KeyFn {
    fn call(
        &self,
        args: &std::collections::HashMap<String, tera::Value>,
    ) -> tera::Result<tera::Value> {
        args.get("name").map_or_else(
            || Err("cannot get parameter 'name'".into()),
            |val| {
                from_value::<String>(val.clone()).map_or_else(
                    |_| Err("cannot get parameter 'name'".into()),
                    |v| {
                        self.kvs
                            .iter()
                            .find(|kv| kv.key == v)
                            .and_then(|kv| to_value(&kv.value).ok())
                            .ok_or_else(|| "not found".into())
                    },
                )
            },
        )
    }
}

/// Render a template with access to KVs
///
/// # Errors
///
/// This function will return an error if rendering fails
pub fn render(template: &str, kvs: Vec<KV>) -> Result<String> {
    let mut tera = Tera::default();
    tera.register_function("key", KeyFn { kvs });
    let res = tera.render_str(template, &Context::new())?;
    Ok(res)
}

#[cfg(test)]
mod tests {
    use insta::assert_debug_snapshot;
    use teller_providers::{config::ProviderInfo, providers::ProviderKind};

    use super::*;

    #[test]
    fn render_template() {
        let kvs = &[KV::from_literal(
            "some/path",
            "k",
            "foobaz",
            ProviderInfo {
                kind: ProviderKind::Inmem,
                name: "test".to_string(),
            },
        )];
        assert_debug_snapshot!(render("hello {{ key(name='k') }}", kvs.to_vec()));
    }
}