Skip to main content

rws_http/entry_point/
rws_env.rs

1//! `RWS_ENV`: which kind of environment the process runs in — `dev`,
2//! `test` or `prod` (the default). Since 18.28.0.
3//!
4//! Read from the process environment only (no config-file key, no flag):
5//! it says how the rest of the configuration should be treated, so it is
6//! set where the process is started. Today it decides whether
7//! `dev_mcp()` mounts its tools without a token; the profiles of
8//! roadmap item #95 build on the same setting.
9
10/// The environment kind named by `RWS_ENV`.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
12pub enum RwsEnv {
13    /// `RWS_ENV=dev` (or `development`): a developer's machine.
14    Dev,
15    /// `RWS_ENV=test`: an automated test run.
16    Test,
17    /// `RWS_ENV=prod` (or `production`), unset, or anything else.
18    #[default]
19    Prod,
20}
21
22impl RwsEnv {
23    /// The environment variable read by [`RwsEnv::current`].
24    pub const VAR: &'static str = "RWS_ENV";
25
26    /// `RWS_ENV` as it is now; unset or unrecognised is [`RwsEnv::Prod`],
27    /// so nothing is opened up by a typo.
28    pub fn current() -> RwsEnv {
29        std::env::var(Self::VAR).ok().and_then(|v| Self::parse(&v)).unwrap_or_default()
30    }
31
32    /// `dev`/`development`, `test`, `prod`/`production` (case-insensitive,
33    /// surrounding spaces ignored); `None` for anything else.
34    pub fn parse(value: &str) -> Option<RwsEnv> {
35        match value.trim().to_ascii_lowercase().as_str() {
36            "dev" | "development" => Some(RwsEnv::Dev),
37            "test" => Some(RwsEnv::Test),
38            "prod" | "production" => Some(RwsEnv::Prod),
39            _ => None,
40        }
41    }
42
43    /// `"dev"`, `"test"` or `"prod"`.
44    pub fn as_str(self) -> &'static str {
45        match self {
46            RwsEnv::Dev => "dev",
47            RwsEnv::Test => "test",
48            RwsEnv::Prod => "prod",
49        }
50    }
51
52    /// `true` for [`RwsEnv::Dev`].
53    pub fn is_dev(self) -> bool {
54        self == RwsEnv::Dev
55    }
56}
57
58impl std::fmt::Display for RwsEnv {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        f.write_str(self.as_str())
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::RwsEnv;
67
68    #[test]
69    fn parses_the_three_names_and_their_long_forms() {
70        assert_eq!(RwsEnv::parse("dev"), Some(RwsEnv::Dev));
71        assert_eq!(RwsEnv::parse(" Development "), Some(RwsEnv::Dev));
72        assert_eq!(RwsEnv::parse("TEST"), Some(RwsEnv::Test));
73        assert_eq!(RwsEnv::parse("prod"), Some(RwsEnv::Prod));
74        assert_eq!(RwsEnv::parse("production"), Some(RwsEnv::Prod));
75    }
76
77    #[test]
78    fn anything_else_is_not_an_environment() {
79        assert_eq!(RwsEnv::parse(""), None);
80        assert_eq!(RwsEnv::parse("staging"), None);
81        assert_eq!(RwsEnv::parse("devel"), None);
82    }
83
84    #[test]
85    fn default_is_prod_and_names_round_trip() {
86        assert_eq!(RwsEnv::default(), RwsEnv::Prod);
87        for env in [RwsEnv::Dev, RwsEnv::Test, RwsEnv::Prod] {
88            assert_eq!(RwsEnv::parse(env.as_str()), Some(env));
89            assert_eq!(env.to_string(), env.as_str());
90        }
91        assert!(RwsEnv::Dev.is_dev());
92        assert!(!RwsEnv::Test.is_dev());
93    }
94}