1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use anyhow::{Ok, Result};
7use providers::Providers;
8use rustc_hash::FxHashMap;
9
10mod providers;
11mod resolve;
12
13pub use resolve::{resolve, resolve_one};
14
15type Hydration = FxHashMap<String, String>;
16
17pub async fn hydrate(
18 env: HashMap<String, String>,
19 cwd: PathBuf,
20 extra_env: HashMap<String, String>,
21) -> Result<HashMap<String, String>> {
22 let mut providers = Providers::new();
23 for value_or_uri in env.values() {
24 providers.add(value_or_uri.clone())?;
25 }
26
27 let mut hydration = providers.resolve(&cwd, &extra_env).await?;
28
29 Ok(env
30 .into_iter()
31 .map(|(key, value_or_uri)| {
32 let value = hydration.remove(&value_or_uri).unwrap_or_else(|| {
33 panic!(
34 "Cannot find {} in {}",
35 value_or_uri,
36 hydration
37 .keys()
38 .cloned()
39 .collect::<Vec<String>>()
40 .join(", ")
41 )
42 });
43 (key, value)
44 })
45 .collect())
46}
47
48pub async fn hydrate_one(
49 value: String,
50 cwd: &Path,
51 extra_env: &HashMap<String, String>,
52) -> Result<String> {
53 let mut providers = Providers::new();
54 providers.add(value.clone())?;
55 let hydration = providers.resolve(cwd, extra_env).await?;
56 Ok(hydration.get(&value).unwrap().to_owned())
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use std::path::PathBuf;
63
64 #[tokio::test]
65 async fn test_hydrate_raw_values_with_empty_extra_env() {
66 let env = HashMap::from([
67 ("KEY1".to_string(), "value1".to_string()),
68 ("KEY2".to_string(), "!value2".to_string()),
69 ]);
70 let result = hydrate(env, PathBuf::from("."), HashMap::new())
71 .await
72 .unwrap();
73 assert_eq!(result.get("KEY1").unwrap(), "value1");
74 assert_eq!(result.get("KEY2").unwrap(), "value2");
75 }
76
77 #[tokio::test]
78 async fn test_hydrate_raw_values_with_extra_env_ignored_by_raw_provider() {
79 let env = HashMap::from([("KEY".to_string(), "rawval".to_string())]);
80 let extra = HashMap::from([("INJECTED".to_string(), "token123".to_string())]);
81 let result = hydrate(env, PathBuf::from("."), extra).await.unwrap();
82 assert_eq!(result.get("KEY").unwrap(), "rawval");
83 }
84
85 #[tokio::test]
86 async fn test_hydrate_one_raw_with_empty_extra_env() {
87 let result = hydrate_one("mytoken".to_string(), &PathBuf::from("."), &HashMap::new())
88 .await
89 .unwrap();
90 assert_eq!(result, "mytoken");
91 }
92
93 #[tokio::test]
94 async fn test_hydrate_one_raw_bang_escape() {
95 let result = hydrate_one("!escaped".to_string(), &PathBuf::from("."), &HashMap::new())
96 .await
97 .unwrap();
98 assert_eq!(result, "escaped");
99 }
100}