1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use anyhow::{bail, Ok, Result};
7use futures::future::try_join_all;
8use once_cell::sync::Lazy;
9use providers::Provider;
10use regex::Regex;
11
12type Hydration = HashMap<String, String>;
13
14mod providers;
15
16pub struct Hydrater {
17 providers: Vec<Box<dyn Provider + Send>>,
18}
19
20impl Hydrater {
21 fn new() -> Self {
22 Self {
23 providers: providers::providers(),
24 }
25 }
26 fn add(&mut self, value: String) -> Result<()> {
27 for provider in self.providers.iter_mut() {
28 if provider.add(value.clone()).is_ok() {
29 return Ok(());
30 }
31 }
32 bail!("No provider found")
33 }
34 async fn resolve(&self, cwd: &Path) -> Result<Hydration> {
35 Ok(try_join_all(self.providers.iter().map(|p| p.resolve(cwd)))
36 .await?
37 .into_iter()
38 .flatten()
39 .collect())
40 }
41}
42
43pub async fn hydrate(
44 env: HashMap<String, String>,
45 cwd: PathBuf,
46) -> Result<HashMap<String, String>> {
47 let mut hydrater = Hydrater::new();
48 for value_or_uri in env.values() {
49 hydrater.add(value_or_uri.clone())?
50 }
51
52 let hydration = hydrater.resolve(&cwd).await?;
53
54 let mut ret: HashMap<String, String> = HashMap::default();
55 for (key, value_or_uri) in env.iter() {
56 ret.insert(
57 key.clone(),
58 hydration
59 .get(value_or_uri)
60 .unwrap_or_else(|| {
61 panic!(
62 "Cannot find {} in {}",
63 value_or_uri,
64 hydration
65 .keys()
66 .cloned()
67 .collect::<Vec<String>>()
68 .join(", ")
69 )
70 })
71 .clone(),
72 );
73 }
74
75 Ok(ret)
76}
77
78pub async fn hydrate_one(value: String, cwd: &Path) -> Result<String> {
79 let mut hydrater = Hydrater::new();
80 hydrater.add(value.clone())?;
81 let hydration = hydrater.resolve(cwd).await?;
82 let hydrated = hydration.get(&value).unwrap().to_owned();
83 Ok(hydrated)
84}
85
86static VAR: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\$\{?(\w+)\}?)").unwrap());
87
88pub fn resolve(
89 kvs: &HashMap<String, String>,
90 existing_vars: &HashMap<String, String>,
91) -> Result<HashMap<String, String>> {
92 kvs.iter()
93 .map(|(key, value)| resolve_one(value, existing_vars).map(|v| (key.clone(), v)))
94 .collect()
95}
96
97pub fn resolve_one(value: &str, existing_vars: &HashMap<String, String>) -> Result<String> {
98 Ok(VAR.captures_iter(value).fold(value.to_string(), |agg, c| {
99 agg.replace(&c[1], existing_vars.get(&c[2]).unwrap_or(&"".to_string()))
100 }))
101}