1use std::{env::current_dir, fmt::Display, fs::File, path::PathBuf};
2
3use anyhow::{anyhow, Context, Error};
4use serde::de::DeserializeOwned;
5
6pub enum Environment {
7 Local,
8 Production,
9}
10
11impl Environment {
12 pub fn as_str(&self) -> &'static str {
13 match self {
14 Self::Local => "local",
15 Self::Production => "production",
16 }
17 }
18}
19
20impl TryFrom<String> for Environment {
21 type Error = anyhow::Error;
22
23 fn try_from(value: String) -> Result<Self, Self::Error> {
24 match value.to_lowercase().as_str() {
25 "local" => Ok(Environment::Local),
26 "production" => Ok(Environment::Production),
27 _ => Err(anyhow!("Couldn't parse environment from '{}'", value)),
28 }
29 }
30}
31
32impl Display for Environment {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 write!(f, "{}", self.as_str())
35 }
36}
37
38pub fn read_configuration<T>(
39 environment_variable: &str,
40 configuration_path_variable: &str,
41) -> Result<T, Error>
42where
43 T: DeserializeOwned,
44{
45 let environment = std::env::var(environment_variable)
46 .unwrap_or_else(|_| "local".into())
47 .try_into()
48 .map_err(|error| Error::from(error).context("Failed to parse environment variable"))?;
49
50 match environment {
51 Environment::Local => load_env_file().context("Failed to load .env")?,
52 _ => (),
53 }
54
55 let configuration_path = std::env::var(configuration_path_variable)
56 .map(|path| PathBuf::from(path))
57 .context("Failed to parse configuration path")?;
58
59 match File::open(&configuration_path) {
60 Ok(file) => serde_json::from_reader(file).map_err(|error| {
61 Error::from(error).context(format!(
62 "Failed to parse configuration file {}",
63 configuration_path.to_string_lossy()
64 ))
65 }),
66 Err(error) => Err(Error::from(error).context(format!(
67 "Failed to open configuration {}",
68 configuration_path.to_string_lossy()
69 ))),
70 }
71}
72
73fn load_env_file() -> Result<(), Error> {
74 let current_dir = current_dir()
75 .context("Failed to get current directory")?
76 .join(".env");
77 let file = std::fs::read_to_string(¤t_dir).context(format!(
78 "Failed to read .env {}",
79 current_dir.to_string_lossy()
80 ))?;
81
82 for line in file.split("\n") {
83 if line.len() == 0 {
84 continue;
85 }
86
87 let mut parts = line.split("=");
88 std::env::set_var(parts.next().unwrap(), parts.next().unwrap());
89 }
90
91 Ok(())
92}