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