1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
19pub enum Backend {
20 Docker,
22 K8s,
24}
25
26pub const GLOBAL_BACKEND_ENV: &str = "FAKECLOUD_CONTAINER_BACKEND";
29
30pub fn backend_choice(per_service_env: &str) -> Backend {
39 match read_choice(per_service_env) {
40 Some(b) => b,
41 None => read_choice(GLOBAL_BACKEND_ENV).unwrap_or(Backend::Docker),
42 }
43}
44
45fn read_choice(var: &str) -> Option<Backend> {
49 match std::env::var(var)
50 .ok()?
51 .trim()
52 .to_ascii_lowercase()
53 .as_str()
54 {
55 "k8s" | "kubernetes" => Some(Backend::K8s),
56 "docker" | "podman" => Some(Backend::Docker),
57 _ => None,
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use std::sync::Mutex;
65
66 static ENV_LOCK: Mutex<()> = Mutex::new(());
68
69 struct EnvGuard(Vec<(String, Option<String>)>);
72 impl Drop for EnvGuard {
73 fn drop(&mut self) {
74 for (k, v) in &self.0 {
75 match v {
76 Some(v) => std::env::set_var(k, v),
77 None => std::env::remove_var(k),
78 }
79 }
80 }
81 }
82
83 fn with_env(vars: &[(&str, Option<&str>)], f: impl FnOnce()) {
84 let _g = ENV_LOCK.lock().unwrap_or_else(|p| p.into_inner());
87 let _restore = EnvGuard(
88 vars.iter()
89 .map(|(k, _)| (k.to_string(), std::env::var(k).ok()))
90 .collect(),
91 );
92 for (k, v) in vars {
93 match v {
94 Some(v) => std::env::set_var(k, v),
95 None => std::env::remove_var(k),
96 }
97 }
98 f();
99 }
100
101 const SVC: &str = "FAKECLOUD_ELASTICACHE_BACKEND";
102
103 #[test]
104 fn defaults_to_docker_when_unset() {
105 with_env(&[(SVC, None), (GLOBAL_BACKEND_ENV, None)], || {
106 assert_eq!(backend_choice(SVC), Backend::Docker);
107 });
108 }
109
110 #[test]
111 fn per_service_k8s_wins() {
112 with_env(&[(SVC, Some("k8s")), (GLOBAL_BACKEND_ENV, None)], || {
113 assert_eq!(backend_choice(SVC), Backend::K8s);
114 });
115 }
116
117 #[test]
118 fn global_k8s_applies_when_service_unset() {
119 with_env(&[(SVC, None), (GLOBAL_BACKEND_ENV, Some("k8s"))], || {
120 assert_eq!(backend_choice(SVC), Backend::K8s);
121 });
122 }
123
124 #[test]
125 fn per_service_docker_overrides_global_k8s() {
126 with_env(
127 &[(SVC, Some("docker")), (GLOBAL_BACKEND_ENV, Some("k8s"))],
128 || {
129 assert_eq!(backend_choice(SVC), Backend::Docker);
130 },
131 );
132 }
133
134 #[test]
135 fn case_insensitive_and_trimmed() {
136 with_env(
137 &[(SVC, Some(" K8s ")), (GLOBAL_BACKEND_ENV, None)],
138 || {
139 assert_eq!(backend_choice(SVC), Backend::K8s);
140 },
141 );
142 }
143
144 #[test]
145 fn unrecognized_service_value_falls_through_to_global() {
146 with_env(
147 &[(SVC, Some("banana")), (GLOBAL_BACKEND_ENV, Some("k8s"))],
148 || {
149 assert_eq!(backend_choice(SVC), Backend::K8s);
150 },
151 );
152 }
153
154 #[test]
155 fn unrecognized_everywhere_is_docker() {
156 with_env(
157 &[(SVC, Some("banana")), (GLOBAL_BACKEND_ENV, Some("nope"))],
158 || {
159 assert_eq!(backend_choice(SVC), Backend::Docker);
160 },
161 );
162 }
163}