Skip to main content

logtail_rust/struct/
env_config.rs

1use serde::Serialize;
2use std::env;
3use std::str::FromStr;
4use strum_macros::{Display, EnumString};
5
6#[derive(Debug, EnumString, Display, PartialEq, Serialize, Clone)]
7pub enum EnvEnum {
8    #[strum(serialize = "local")]
9    Local,
10    #[strum(serialize = "qa")]
11    QA,
12    #[strum(serialize = "preprod")]
13    PreProd,
14    #[strum(serialize = "prod")]
15    Prod,
16}
17
18pub struct EnvConfig {
19    pub app_version: String,
20    pub environment: EnvEnum,
21    pub logs_source_token: String,
22    pub verbose: bool,
23}
24
25impl Default for EnvConfig {
26    /// Configures environment with the default app version as _this_ cargo package version.
27    /// default level for Logger is verbose
28    fn default() -> Self {
29        Self::new(env!("CARGO_PKG_VERSION").to_string(), true)
30    }
31}
32impl EnvConfig {
33    pub fn from_values(
34        app_version: String,
35        environment: EnvEnum,
36        logs_source_token: String,
37        verbose: bool,
38    ) -> Self {
39        EnvConfig {
40            app_version,
41            environment,
42            logs_source_token,
43            verbose,
44        }
45    }
46
47    pub fn new(app_version: String, verbose: bool) -> Self {
48        dotenv::dotenv().ok();
49        let environment_string = env::var("ENVIRONMENT").expect("missing variable: ENVIRONMENT");
50        let environment = EnvEnum::from_str(&environment_string).unwrap();
51        let logs_source_token =
52            env::var("LOGS_SOURCE_TOKEN").expect("missing variable: LOGS_SOURCE_TOKEN");
53
54        EnvConfig {
55            app_version,
56            environment,
57            logs_source_token,
58            verbose,
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use serial_test::serial;
67
68    // --- EnvEnum tests ---
69
70    #[test]
71    fn display_all_variants() {
72        assert_eq!(EnvEnum::Local.to_string(), "local");
73        assert_eq!(EnvEnum::QA.to_string(), "qa");
74        assert_eq!(EnvEnum::PreProd.to_string(), "preprod");
75        assert_eq!(EnvEnum::Prod.to_string(), "prod");
76    }
77
78    #[test]
79    fn parse_valid_variants() {
80        assert_eq!(EnvEnum::from_str("local").unwrap(), EnvEnum::Local);
81        assert_eq!(EnvEnum::from_str("qa").unwrap(), EnvEnum::QA);
82        assert_eq!(EnvEnum::from_str("preprod").unwrap(), EnvEnum::PreProd);
83        assert_eq!(EnvEnum::from_str("prod").unwrap(), EnvEnum::Prod);
84    }
85
86    #[test]
87    fn parse_invalid_returns_err() {
88        assert!(EnvEnum::from_str("staging").is_err());
89        assert!(EnvEnum::from_str("LOCAL").is_err());
90        assert!(EnvEnum::from_str("").is_err());
91    }
92
93    #[test]
94    fn equality() {
95        assert_eq!(EnvEnum::Local, EnvEnum::Local);
96        assert_eq!(EnvEnum::Prod, EnvEnum::Prod);
97        assert_ne!(EnvEnum::Local, EnvEnum::Prod);
98        assert_ne!(EnvEnum::QA, EnvEnum::PreProd);
99    }
100
101    #[test]
102    fn serde_serialize() {
103        assert_eq!(serde_json::to_string(&EnvEnum::Local).unwrap(), "\"Local\"");
104        assert_eq!(serde_json::to_string(&EnvEnum::QA).unwrap(), "\"QA\"");
105        assert_eq!(
106            serde_json::to_string(&EnvEnum::PreProd).unwrap(),
107            "\"PreProd\""
108        );
109        assert_eq!(serde_json::to_string(&EnvEnum::Prod).unwrap(), "\"Prod\"");
110    }
111
112    // --- EnvConfig::from_values tests ---
113
114    #[test]
115    fn from_values_sets_all_fields() {
116        let config = EnvConfig::from_values(
117            "1.2.3".to_string(),
118            EnvEnum::Prod,
119            "my-token".to_string(),
120            false,
121        );
122
123        assert_eq!(config.app_version, "1.2.3");
124        assert_eq!(config.environment, EnvEnum::Prod);
125        assert_eq!(config.logs_source_token, "my-token");
126        assert!(!config.verbose);
127    }
128
129    // --- EnvConfig::new tests (env-var dependent, must run serially) ---
130
131    #[test]
132    #[serial]
133    fn new_reads_env_vars() {
134        env::set_var("ENVIRONMENT", "qa");
135        env::set_var("LOGS_SOURCE_TOKEN", "test-token-123");
136
137        let config = EnvConfig::new("0.5.0".to_string(), true);
138
139        assert_eq!(config.app_version, "0.5.0");
140        assert_eq!(config.environment, EnvEnum::QA);
141        assert_eq!(config.logs_source_token, "test-token-123");
142        assert!(config.verbose);
143    }
144
145    #[test]
146    #[serial]
147    fn default_uses_cargo_version() {
148        env::set_var("ENVIRONMENT", "local");
149        env::set_var("LOGS_SOURCE_TOKEN", "token");
150
151        let config = EnvConfig::default();
152
153        assert_eq!(config.app_version, env!("CARGO_PKG_VERSION"));
154        assert!(config.verbose);
155    }
156
157    #[test]
158    #[serial]
159    #[should_panic(expected = "missing variable: ENVIRONMENT")]
160    fn new_panics_missing_environment() {
161        env::remove_var("ENVIRONMENT");
162        env::set_var("LOGS_SOURCE_TOKEN", "token");
163
164        EnvConfig::new("1.0.0".to_string(), false);
165    }
166
167    #[test]
168    #[serial]
169    #[should_panic]
170    fn new_panics_invalid_environment() {
171        env::set_var("ENVIRONMENT", "staging");
172        env::set_var("LOGS_SOURCE_TOKEN", "token");
173
174        EnvConfig::new("1.0.0".to_string(), false);
175    }
176
177    #[test]
178    #[serial]
179    #[should_panic(expected = "missing variable: LOGS_SOURCE_TOKEN")]
180    fn new_panics_missing_token() {
181        env::set_var("ENVIRONMENT", "local");
182        env::remove_var("LOGS_SOURCE_TOKEN");
183
184        EnvConfig::new("1.0.0".to_string(), false);
185    }
186}