mk_lib/
utils.rs

1use std::{
2  fmt,
3  fs,
4};
5
6use anyhow::Context as _;
7use hashbrown::HashMap;
8use serde::de::{
9  self,
10  MapAccess,
11  Visitor,
12};
13use serde::{
14  Deserialize,
15  Deserializer,
16};
17use serde_json::Value as JsonValue;
18
19#[allow(dead_code)]
20#[derive(Debug)]
21enum AnyValue {
22  String(String),
23  Number(serde_json::Number),
24  Bool(bool),
25}
26
27impl fmt::Display for AnyValue {
28  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29    match self {
30      AnyValue::String(s) => write!(f, "{}", s),
31      AnyValue::Number(n) => write!(f, "{}", n),
32      AnyValue::Bool(b) => write!(f, "{}", b),
33    }
34  }
35}
36
37impl<'de> Deserialize<'de> for AnyValue {
38  fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
39  where
40    D: Deserializer<'de>,
41  {
42    let value: JsonValue = Deserialize::deserialize(deserializer)?;
43    match value {
44      JsonValue::String(s) => Ok(AnyValue::String(s)),
45      JsonValue::Number(n) => Ok(AnyValue::Number(n)),
46      JsonValue::Bool(b) => Ok(AnyValue::Bool(b)),
47      _ => Err(de::Error::custom("expected a string, number, or boolean")),
48    }
49  }
50}
51
52pub(crate) fn deserialize_environment<'de, D>(deserializer: D) -> Result<HashMap<String, String>, D::Error>
53where
54  D: Deserializer<'de>,
55{
56  struct EnvironmentVisitor;
57
58  impl<'de> Visitor<'de> for EnvironmentVisitor {
59    type Value = HashMap<String, String>;
60
61    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
62      formatter.write_str("a map of strings to any value (string, int, or bool)")
63    }
64
65    fn visit_map<M>(self, mut access: M) -> Result<HashMap<String, String>, M::Error>
66    where
67      M: MapAccess<'de>,
68    {
69      let mut map = HashMap::new();
70      while let Some((key, value)) = access.next_entry::<String, AnyValue>()? {
71        map.insert(key, value.to_string());
72      }
73      Ok(map)
74    }
75  }
76
77  deserializer.deserialize_map(EnvironmentVisitor)
78}
79
80pub(crate) fn load_env_files(env_files: &[String]) -> anyhow::Result<HashMap<String, String>> {
81  let mut local_env: HashMap<String, String> = HashMap::new();
82  for env_file in env_files {
83    let contents =
84      fs::read_to_string(env_file).with_context(|| format!("Failed to read env file - {}", env_file))?;
85
86    for line in contents.lines() {
87      if let Some((key, value)) = line.split_once('=') {
88        local_env.insert(key.trim().to_string(), value.trim().to_string());
89      }
90    }
91  }
92
93  Ok(local_env)
94}