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
63
64
65
66
67
68
69
70
71
72
73
74
use std::collections::HashMap;

use anyhow::{bail, Ok, Result};
use futures::future::try_join_all;
use lazy_static::lazy_static;
use providers::Provider;
use regex::Regex;

type Hydration = HashMap<String, String>;

mod providers;

pub struct Hydrater {
    providers: Vec<Box<dyn Provider>>,
}

impl Hydrater {
    fn new() -> Self {
        Self {
            providers: providers::providers(),
        }
    }
    fn add(&mut self, value: String) -> Result<()> {
        for provider in self.providers.iter_mut() {
            if provider.add(value.clone()).is_ok() {
                return Ok(());
            }
        }
        bail!("No provider found")
    }
    async fn resolve(&self) -> Result<Hydration> {
        Ok(try_join_all(self.providers.iter().map(|p| p.resolve()))
            .await?
            .into_iter()
            .flatten()
            .collect())
    }
}

pub async fn hydrate(env: HashMap<String, String>) -> Result<HashMap<String, String>> {
    let mut hydrater = Hydrater::new();
    for value_or_uri in env.values() {
        hydrater.add(value_or_uri.clone())?
    }

    let hydration = hydrater.resolve().await?;

    let mut ret: HashMap<String, String> = HashMap::default();
    for (key, value_or_uri) in env.iter() {
        ret.insert(key.clone(), hydration.get(value_or_uri).unwrap().clone());
    }

    Ok(ret)
}

lazy_static! {
    static ref VAR: Regex = Regex::new(r"(\$\{?(\w+)\}?)").unwrap();
}

pub fn resolve_env(
    kvs: &HashMap<String, String>,
    existing_vars: &HashMap<String, String>,
) -> Result<HashMap<String, String>> {
    let ret = kvs
        .iter()
        .map(|(key, value)| {
            let hydration = VAR.captures_iter(value).fold(value.clone(), |agg, c| {
                agg.replace(&c[1], existing_vars.get(&c[2]).unwrap_or(&"".to_string()))
            });
            (key.clone(), hydration)
        })
        .collect();
    Ok(ret)
}