Skip to main content

everruns_core/
deployment.rs

1//! Deployment grade configuration
2//!
3//! This module defines the deployment grade (environment tier) which controls
4//! which features and capabilities are available in each environment.
5//!
6//! Grades:
7//! - `dev`: Development environment, all experimental features enabled
8//! - `poc`: Proof of concept / demo environment
9//! - `preview`: Preview/staging environment
10//! - `prod`: Production environment, only stable features
11
12use std::fmt;
13use std::str::FromStr;
14
15/// Deployment grade (environment tier)
16///
17/// Controls which features are available. More permissive grades include
18/// experimental features that may not be stable or secure for production.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub enum DeploymentGrade {
21    /// Development environment - all experimental features enabled
22    Dev,
23    /// Proof of concept / demo environment
24    Poc,
25    /// Preview/staging environment
26    Preview,
27    /// Production environment - only stable features
28    #[default]
29    Prod,
30}
31
32impl DeploymentGrade {
33    /// Returns true if this is a development environment
34    pub fn is_dev(&self) -> bool {
35        matches!(self, DeploymentGrade::Dev)
36    }
37
38    /// Returns true if experimental features should be enabled
39    ///
40    /// Currently only enabled in dev environments
41    pub fn experimental_features_enabled(&self) -> bool {
42        matches!(self, DeploymentGrade::Dev)
43    }
44
45    /// Parse from environment variable
46    ///
47    /// Reads DEPLOYMENT_GRADE env var. Returns Dev if DEV_MODE=true,
48    /// otherwise defaults to Prod.
49    pub fn from_env() -> Self {
50        // Check explicit DEPLOYMENT_GRADE first
51        if let Ok(grade) = std::env::var("DEPLOYMENT_GRADE") {
52            return grade.parse().unwrap_or_default();
53        }
54
55        // Fall back to DEV_MODE for backwards compatibility
56        let dev_mode = std::env::var("DEV_MODE")
57            .map(|v| v == "true" || v == "1")
58            .unwrap_or(false);
59
60        if dev_mode {
61            DeploymentGrade::Dev
62        } else {
63            DeploymentGrade::Prod
64        }
65    }
66}
67
68impl fmt::Display for DeploymentGrade {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            DeploymentGrade::Dev => write!(f, "dev"),
72            DeploymentGrade::Poc => write!(f, "poc"),
73            DeploymentGrade::Preview => write!(f, "preview"),
74            DeploymentGrade::Prod => write!(f, "prod"),
75        }
76    }
77}
78
79impl FromStr for DeploymentGrade {
80    type Err = String;
81
82    fn from_str(s: &str) -> Result<Self, Self::Err> {
83        match s.to_lowercase().as_str() {
84            "dev" | "development" => Ok(DeploymentGrade::Dev),
85            "poc" => Ok(DeploymentGrade::Poc),
86            "preview" | "staging" => Ok(DeploymentGrade::Preview),
87            "prod" | "production" => Ok(DeploymentGrade::Prod),
88            _ => Err(format!(
89                "Unknown deployment grade: '{}'. Valid values: dev, poc, preview, prod",
90                s
91            )),
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_parse_grades() {
102        assert_eq!(
103            "dev".parse::<DeploymentGrade>().unwrap(),
104            DeploymentGrade::Dev
105        );
106        assert_eq!(
107            "development".parse::<DeploymentGrade>().unwrap(),
108            DeploymentGrade::Dev
109        );
110        assert_eq!(
111            "poc".parse::<DeploymentGrade>().unwrap(),
112            DeploymentGrade::Poc
113        );
114        assert_eq!(
115            "preview".parse::<DeploymentGrade>().unwrap(),
116            DeploymentGrade::Preview
117        );
118        assert_eq!(
119            "staging".parse::<DeploymentGrade>().unwrap(),
120            DeploymentGrade::Preview
121        );
122        assert_eq!(
123            "prod".parse::<DeploymentGrade>().unwrap(),
124            DeploymentGrade::Prod
125        );
126        assert_eq!(
127            "production".parse::<DeploymentGrade>().unwrap(),
128            DeploymentGrade::Prod
129        );
130    }
131
132    #[test]
133    fn test_case_insensitive() {
134        assert_eq!(
135            "DEV".parse::<DeploymentGrade>().unwrap(),
136            DeploymentGrade::Dev
137        );
138        assert_eq!(
139            "PROD".parse::<DeploymentGrade>().unwrap(),
140            DeploymentGrade::Prod
141        );
142    }
143
144    #[test]
145    fn test_default() {
146        assert_eq!(DeploymentGrade::default(), DeploymentGrade::Prod);
147    }
148
149    #[test]
150    fn test_experimental_features() {
151        assert!(DeploymentGrade::Dev.experimental_features_enabled());
152        assert!(!DeploymentGrade::Poc.experimental_features_enabled());
153        assert!(!DeploymentGrade::Preview.experimental_features_enabled());
154        assert!(!DeploymentGrade::Prod.experimental_features_enabled());
155    }
156
157    #[test]
158    fn test_display() {
159        assert_eq!(DeploymentGrade::Dev.to_string(), "dev");
160        assert_eq!(DeploymentGrade::Poc.to_string(), "poc");
161        assert_eq!(DeploymentGrade::Preview.to_string(), "preview");
162        assert_eq!(DeploymentGrade::Prod.to_string(), "prod");
163    }
164}