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
75
76
77
78
79
80
81
82
83
84
85
use std::{
    collections::HashMap,
    path::{Path, PathBuf},
};

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

type Hydration = HashMap<String, String>;

mod providers;

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

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, cwd: &Path) -> Result<Hydration> {
        Ok(try_join_all(self.providers.iter().map(|p| p.resolve(cwd)))
            .await?
            .into_iter()
            .flatten()
            .collect())
    }
}

pub async fn hydrate(
    env: HashMap<String, String>,
    cwd: PathBuf,
) -> 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(&cwd).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)
}

pub async fn hydrate_one(value: String, cwd: &Path) -> Result<String> {
    let mut hydrater = Hydrater::new();
    hydrater.add(value.clone())?;
    let hydration = hydrater.resolve(cwd).await?;
    let hydrated = hydration.get(&value).unwrap().to_owned();
    Ok(hydrated)
}

static VAR: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\$\{?(\w+)\}?)").unwrap());

pub fn resolve(
    kvs: &HashMap<String, String>,
    existing_vars: &HashMap<String, String>,
) -> Result<HashMap<String, String>> {
    kvs.iter()
        .map(|(key, value)| resolve_one(value, existing_vars).map(|v| (key.clone(), v)))
        .collect()
}

pub fn resolve_one(value: &str, existing_vars: &HashMap<String, String>) -> Result<String> {
    Ok(VAR.captures_iter(value).fold(value.to_string(), |agg, c| {
        agg.replace(&c[1], existing_vars.get(&c[2]).unwrap_or(&"".to_string()))
    }))
}