1use std::path::Path;
2use std::sync::OnceLock;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum Platform {
7 MacOS,
9 LinuxNative,
11 LinuxNoKvm,
13}
14
15impl Platform {
16 pub fn needs_lima(self) -> bool {
18 match self {
19 Platform::MacOS | Platform::LinuxNoKvm => true,
20 Platform::LinuxNative => false,
21 }
22 }
23
24 pub fn has_kvm(self) -> bool {
26 matches!(self, Platform::LinuxNative)
27 }
28}
29
30impl std::fmt::Display for Platform {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Platform::MacOS => write!(f, "macOS (via Lima)"),
34 Platform::LinuxNative => write!(f, "Linux (native KVM)"),
35 Platform::LinuxNoKvm => write!(f, "Linux (via Lima, no KVM)"),
36 }
37 }
38}
39
40static DETECTED: OnceLock<Platform> = OnceLock::new();
42
43pub fn current() -> Platform {
45 *DETECTED.get_or_init(detect)
46}
47
48fn detect() -> Platform {
49 if cfg!(target_os = "macos") {
50 Platform::MacOS
51 } else if cfg!(target_os = "linux") {
52 if Path::new("/dev/kvm").exists() {
53 Platform::LinuxNative
54 } else {
55 Platform::LinuxNoKvm
56 }
57 } else {
58 Platform::MacOS
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn test_detect_returns_consistent_result() {
69 let a = current();
70 let b = current();
71 assert_eq!(a, b);
72 }
73
74 #[test]
75 fn test_platform_display() {
76 assert_eq!(Platform::MacOS.to_string(), "macOS (via Lima)");
77 assert_eq!(Platform::LinuxNative.to_string(), "Linux (native KVM)");
78 assert_eq!(Platform::LinuxNoKvm.to_string(), "Linux (via Lima, no KVM)");
79 }
80
81 #[test]
82 fn test_needs_lima() {
83 assert!(Platform::MacOS.needs_lima());
84 assert!(!Platform::LinuxNative.needs_lima());
85 assert!(Platform::LinuxNoKvm.needs_lima());
86 }
87
88 #[test]
89 fn test_has_kvm() {
90 assert!(!Platform::MacOS.has_kvm());
91 assert!(Platform::LinuxNative.has_kvm());
92 assert!(!Platform::LinuxNoKvm.has_kvm());
93 }
94
95 #[test]
96 fn test_current_platform_valid() {
97 let p = current();
98 let _ = p.needs_lima();
100 let _ = p.has_kvm();
101 }
102}