1use serde::{Deserialize, Serialize};
15use std::path::Path;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum Platform {
20 Linux,
22 MacOS,
24 Container,
26 PepitaMicroVM,
28 Wos,
30 Native,
32}
33
34impl Platform {
35 #[must_use]
37 pub const fn supports_isolation(&self) -> bool {
38 matches!(self, Self::Container | Self::PepitaMicroVM | Self::Wos)
39 }
40
41 #[must_use]
43 pub const fn supports_cgroups(&self) -> bool {
44 matches!(self, Self::Linux | Self::Container)
45 }
46
47 #[must_use]
49 pub const fn supports_systemd(&self) -> bool {
50 matches!(self, Self::Linux)
51 }
52
53 #[must_use]
55 pub const fn supports_launchd(&self) -> bool {
56 matches!(self, Self::MacOS)
57 }
58
59 #[must_use]
61 pub const fn name(&self) -> &'static str {
62 match self {
63 Self::Linux => "linux",
64 Self::MacOS => "macos",
65 Self::Container => "container",
66 Self::PepitaMicroVM => "pepita",
67 Self::Wos => "wos",
68 Self::Native => "native",
69 }
70 }
71}
72
73impl std::fmt::Display for Platform {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.name())
76 }
77}
78
79#[must_use]
89pub fn detect_platform() -> Platform {
90 if cfg!(target_arch = "wasm32") || std::env::var("WOS_KERNEL").is_ok() {
92 return Platform::Wos;
93 }
94
95 if is_pepita_vm() {
97 return Platform::PepitaMicroVM;
98 }
99
100 if is_container() {
102 return Platform::Container;
103 }
104
105 #[cfg(target_os = "linux")]
107 if is_systemd_available() {
108 return Platform::Linux;
109 }
110
111 #[cfg(target_os = "macos")]
113 {
114 return Platform::MacOS;
115 }
116
117 Platform::Native
119}
120
121fn is_pepita_vm() -> bool {
123 if std::env::var("PEPITA_VM").is_ok() {
125 return true;
126 }
127
128 Path::new("/dev/virtio-ports").exists()
130}
131
132fn is_container() -> bool {
134 if Path::new("/.dockerenv").exists() {
136 return true;
137 }
138
139 if let Ok(cgroup) = std::fs::read_to_string("/proc/1/cgroup")
141 && (cgroup.contains("docker")
142 || cgroup.contains("containerd")
143 || cgroup.contains("kubepods")
144 || cgroup.contains("lxc"))
145 {
146 return true;
147 }
148
149 if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
151 return true;
152 }
153
154 false
155}
156
157#[cfg(target_os = "linux")]
159fn is_systemd_available() -> bool {
160 Path::new("/run/systemd/system").exists()
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
172 fn test_platform_display() {
173 assert_eq!(Platform::Linux.to_string(), "linux");
174 assert_eq!(Platform::MacOS.to_string(), "macos");
175 assert_eq!(Platform::Container.to_string(), "container");
176 assert_eq!(Platform::PepitaMicroVM.to_string(), "pepita");
177 assert_eq!(Platform::Wos.to_string(), "wos");
178 assert_eq!(Platform::Native.to_string(), "native");
179 }
180
181 #[test]
182 fn test_platform_name() {
183 assert_eq!(Platform::Linux.name(), "linux");
184 assert_eq!(Platform::MacOS.name(), "macos");
185 assert_eq!(Platform::Container.name(), "container");
186 assert_eq!(Platform::PepitaMicroVM.name(), "pepita");
187 assert_eq!(Platform::Wos.name(), "wos");
188 assert_eq!(Platform::Native.name(), "native");
189 }
190
191 #[test]
192 fn test_platform_supports_isolation() {
193 assert!(!Platform::Linux.supports_isolation());
194 assert!(!Platform::MacOS.supports_isolation());
195 assert!(Platform::Container.supports_isolation());
196 assert!(Platform::PepitaMicroVM.supports_isolation());
197 assert!(Platform::Wos.supports_isolation());
198 assert!(!Platform::Native.supports_isolation());
199 }
200
201 #[test]
202 fn test_platform_supports_cgroups() {
203 assert!(Platform::Linux.supports_cgroups());
204 assert!(!Platform::MacOS.supports_cgroups());
205 assert!(Platform::Container.supports_cgroups());
206 assert!(!Platform::PepitaMicroVM.supports_cgroups());
207 assert!(!Platform::Wos.supports_cgroups());
208 assert!(!Platform::Native.supports_cgroups());
209 }
210
211 #[test]
212 fn test_platform_supports_systemd() {
213 assert!(Platform::Linux.supports_systemd());
214 assert!(!Platform::MacOS.supports_systemd());
215 assert!(!Platform::Container.supports_systemd());
216 assert!(!Platform::PepitaMicroVM.supports_systemd());
217 assert!(!Platform::Wos.supports_systemd());
218 assert!(!Platform::Native.supports_systemd());
219 }
220
221 #[test]
222 fn test_platform_supports_launchd() {
223 assert!(!Platform::Linux.supports_launchd());
224 assert!(Platform::MacOS.supports_launchd());
225 assert!(!Platform::Container.supports_launchd());
226 assert!(!Platform::PepitaMicroVM.supports_launchd());
227 assert!(!Platform::Wos.supports_launchd());
228 assert!(!Platform::Native.supports_launchd());
229 }
230
231 #[test]
232 fn test_platform_equality() {
233 assert_eq!(Platform::Linux, Platform::Linux);
234 assert_ne!(Platform::Linux, Platform::MacOS);
235 }
236
237 #[test]
238 fn test_platform_clone() {
239 let p1 = Platform::Container;
240 let p2 = p1;
241 assert_eq!(p1, p2);
242 }
243
244 #[test]
245 fn test_platform_debug() {
246 let debug = format!("{:?}", Platform::Linux);
247 assert!(debug.contains("Linux"));
248 }
249
250 #[test]
255 fn test_detect_platform_returns_valid() {
256 let platform = detect_platform();
258 let valid = matches!(
260 platform,
261 Platform::Linux
262 | Platform::MacOS
263 | Platform::Container
264 | Platform::PepitaMicroVM
265 | Platform::Wos
266 | Platform::Native
267 );
268 assert!(valid);
269 }
270
271 #[test]
272 fn test_is_container_false_on_host() {
273 let result = is_container();
276 let _ = result;
278 }
279
280 #[test]
281 fn test_is_pepita_vm_false_on_host() {
282 let result = is_pepita_vm();
284 let _ = result;
286 }
287
288 #[test]
293 fn test_platform_serialize() {
294 let json = serde_json::to_string(&Platform::Linux).unwrap();
295 assert!(json.contains("Linux"));
296 }
297
298 #[test]
299 fn test_platform_deserialize() {
300 let platform: Platform = serde_json::from_str("\"Linux\"").unwrap();
301 assert_eq!(platform, Platform::Linux);
302 }
303
304 #[test]
305 fn test_platform_roundtrip() {
306 for platform in [
307 Platform::Linux,
308 Platform::MacOS,
309 Platform::Container,
310 Platform::PepitaMicroVM,
311 Platform::Wos,
312 Platform::Native,
313 ] {
314 let json = serde_json::to_string(&platform).unwrap();
315 let deserialized: Platform = serde_json::from_str(&json).unwrap();
316 assert_eq!(platform, deserialized);
317 }
318 }
319
320 #[test]
325 fn test_platform_hash() {
326 use std::collections::HashSet;
327
328 let mut set = HashSet::new();
329 set.insert(Platform::Linux);
330 set.insert(Platform::MacOS);
331 set.insert(Platform::Linux); assert_eq!(set.len(), 2);
334 assert!(set.contains(&Platform::Linux));
335 assert!(set.contains(&Platform::MacOS));
336 assert!(!set.contains(&Platform::Container));
337 }
338}