Skip to main content

lamina_platform/
detection.rs

1//! Host system detection functions.
2
3/// Detect the host system's architecture only.
4///
5/// Returns a string representing the detected architecture: "x86_64", "aarch64", etc.
6///
7/// Falls back to "x86_64" if detection fails.
8pub fn detect_host_architecture_only() -> &'static str {
9    #[cfg(target_arch = "x86_64")]
10    return "x86_64";
11    #[cfg(target_arch = "aarch64")]
12    return "aarch64";
13    #[cfg(target_arch = "wasm32")]
14    return "wasm32";
15    #[cfg(target_arch = "wasm64")]
16    return "wasm64";
17    #[cfg(target_arch = "riscv32")]
18    return "riscv32";
19    #[cfg(target_arch = "riscv64")]
20    return "riscv64";
21
22    // Default fallback
23    #[allow(unreachable_code)]
24    "x86_64"
25}
26
27/// Detect the host system's operating system only.
28///
29/// Returns a string representing the detected operating system: "linux", "macos", "windows", etc.
30///
31/// Falls back to "unknown" if detection fails.
32pub fn detect_host_os() -> &'static str {
33    #[cfg(target_os = "linux")]
34    return "linux";
35    #[cfg(target_os = "macos")]
36    return "macos";
37    #[cfg(target_os = "windows")]
38    return "windows";
39    #[cfg(target_os = "freebsd")]
40    return "freebsd";
41    #[cfg(target_os = "openbsd")]
42    return "openbsd";
43    #[cfg(target_os = "netbsd")]
44    return "netbsd";
45    #[cfg(target_os = "dragonfly")]
46    return "dragonfly";
47    #[cfg(target_os = "redox")]
48    return "redox";
49
50    // Default fallback
51    #[allow(unreachable_code)]
52    "unknown"
53}
54
55/// Detect the host system's architecture and operating system combination.
56///
57/// Returns a string representing the detected architecture and host system combination.
58///
59/// # Supported Targets
60/// - x86_64_unknown, x86_64_linux, x86_64_windows, x86_64_macos
61/// - aarch64_unknown, aarch64_macos, aarch64_linux, aarch64_windows
62/// - wasm32_unknown, wasm64_unknown
63/// - riscv32_unknown, riscv64_unknown
64/// - riscv128_unknown (nightly feature only)
65///
66/// Falls back to "x86_64_unknown" for unsupported combinations.
67///
68/// # Deprecated
69/// This function is deprecated. Use `detect_host().to_str()` instead for a more structured approach.
70#[deprecated(since = "0.0.8", note = "Use `detect_host().to_str()` instead")]
71pub fn detect_host_architecture() -> &'static str {
72    let arch = detect_host_architecture_only();
73    let os = detect_host_os();
74    // For backward compatibility, return the combined format
75    // This will be removed once the deprecation period is over
76    match (arch, os) {
77        ("x86_64", "linux") => "x86_64_linux",
78        ("x86_64", "macos") => "x86_64_macos",
79        ("x86_64", "windows") => "x86_64_windows",
80        ("aarch64", "linux") => "aarch64_linux",
81        ("aarch64", "macos") => "aarch64_macos",
82        ("aarch64", "windows") => "aarch64_windows",
83        ("wasm32", _) => "wasm32_unknown",
84        ("wasm64", _) => "wasm64_unknown",
85        ("riscv32", _) => "riscv32_unknown",
86        ("riscv64", _) => "riscv64_unknown",
87        #[cfg(feature = "nightly")]
88        ("riscv128", _) => "riscv128_unknown",
89        _ => {
90            // Fallback for unsupported combinations
91            match arch {
92                "x86_64" => "x86_64_unknown",
93                "aarch64" => "aarch64_unknown",
94                _ => "x86_64_unknown",
95            }
96        }
97    }
98}
99
100/// Get the number of available CPU cores.
101///
102/// Returns the number of logical CPU cores available on the system.
103/// Falls back to 1 if detection fails.
104///
105/// # Examples
106///
107/// ```
108/// use lamina_platform::detection::cpu_count;
109/// let cores = cpu_count();
110/// println!("System has {} CPU cores", cores);
111/// ```
112pub fn cpu_count() -> usize {
113    #[cfg(target_os = "linux")]
114    {
115        // Try reading from /proc/cpuinfo first (most reliable)
116        if let Ok(content) = std::fs::read_to_string("/proc/cpuinfo") {
117            let count = content
118                .lines()
119                .filter(|line| line.starts_with("processor"))
120                .count();
121            if count > 0 {
122                return count;
123            }
124        }
125
126        // Fallback to sysconf
127        unsafe {
128            unsafe extern "C" {
129                fn sysconf(name: i32) -> i64;
130            }
131            const _SC_NPROCESSORS_ONLN: i32 = 84;
132            let count = sysconf(_SC_NPROCESSORS_ONLN);
133            if count > 0 {
134                return count as usize;
135            }
136        }
137    }
138
139    #[cfg(target_os = "macos")]
140    {
141        use std::ffi::CString;
142        unsafe {
143            unsafe extern "C" {
144                fn sysctlbyname(
145                    name: *const i8,
146                    oldp: *mut std::ffi::c_void,
147                    oldlenp: *mut usize,
148                    newp: *const std::ffi::c_void,
149                    newlen: usize,
150                ) -> i32;
151            }
152            // SAFETY: "hw.ncpu" contains no interior nul bytes, so CString::new never fails.
153            #[allow(clippy::unwrap_used)]
154            let name = CString::new("hw.ncpu").unwrap();
155            let mut count: u32 = 0;
156            let mut size = std::mem::size_of::<u32>();
157            if sysctlbyname(
158                name.as_ptr(),
159                &mut count as *mut _ as *mut std::ffi::c_void,
160                &mut size,
161                std::ptr::null(),
162                0,
163            ) == 0
164            {
165                return count as usize;
166            }
167        }
168    }
169
170    #[cfg(target_os = "windows")]
171    {
172        unsafe {
173            unsafe extern "system" {
174                fn GetSystemInfo(lpSystemInfo: *mut SystemInfo);
175            }
176            // Field order matches the Win32 SYSTEM_INFO layout (repr(C)); names
177            // are snake_case since only the layout is ABI-significant.
178            #[repr(C)]
179            struct SystemInfo {
180                processor_architecture: u16,
181                reserved: u16,
182                page_size: u32,
183                minimum_application_address: *mut std::ffi::c_void,
184                maximum_application_address: *mut std::ffi::c_void,
185                active_processor_mask: *mut u32,
186                number_of_processors: u32,
187                processor_type: u32,
188                allocation_granularity: u32,
189                processor_level: u16,
190                processor_revision: u16,
191            }
192            let mut info = std::mem::zeroed::<SystemInfo>();
193            GetSystemInfo(&mut info);
194            info.number_of_processors as usize
195        }
196    }
197
198    // Fallback for other platforms
199    #[cfg(not(target_os = "windows"))]
200    {
201        1
202    }
203}