ch_grafana_cache/
variables.rs

1use std::collections::HashMap;
2
3use itertools::Itertools;
4
5lazy_static::lazy_static! {
6    pub static ref VARIABLE_RE: regex::Regex = regex::Regex::new(r#"\$\{(.*?)\}"#).unwrap();
7}
8
9pub type VariablesAssignment<'a> = HashMap<&'a str, String>;
10
11#[derive(thiserror::Error, Debug)]
12enum SubsError {
13    #[error("Variable {0} not found")]
14    NotFound(String),
15}
16
17fn substitute_variable(
18    cap: &regex::Captures,
19    variables: &VariablesAssignment<'_>,
20) -> Result<String, SubsError> {
21    let name = cap.get(1).unwrap().as_str();
22    variables
23        .get(name)
24        .ok_or_else(|| SubsError::NotFound(name.into()))
25        .cloned()
26        .map(|s| s.to_string())
27}
28pub fn substitute_variables(
29    sql: &str,
30    variables: &VariablesAssignment<'_>,
31) -> anyhow::Result<String> {
32    let mut errors = Vec::<SubsError>::default();
33
34    let sql = VARIABLE_RE
35        .replace_all(sql, |cap: &regex::Captures| {
36            substitute_variable(cap, variables).unwrap_or_else(|e| {
37                errors.push(e);
38                "ERROR".into()
39            })
40        })
41        .into_owned();
42    if !errors.is_empty() {
43        anyhow::bail!(
44            "Encountered substitution errors:\n  {}",
45            errors.into_iter().map(|e| e.to_string()).join("\n  ")
46        );
47    }
48    Ok(sql)
49}
50
51#[cfg(test)]
52mod test {
53    #[test]
54    fn substitute_variables() -> anyhow::Result<()> {
55        assert_eq!(
56            "SELECT * FROM test",
57            super::substitute_variables(
58                "SELECT * FROM ${table}",
59                &std::collections::HashMap::from([("table", "test".into())])
60            )?
61        );
62        // Missing variable
63        assert!(super::substitute_variables("${table}", &Default::default()).is_err());
64        Ok(())
65    }
66}