hardware_query/
cpu.rs

1use crate::{HardwareQueryError, Result};
2use serde::{Deserialize, Serialize};
3use std::fmt;
4use sysinfo::System;
5
6/// CPU vendor information
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub enum CPUVendor {
9    Intel,
10    AMD,
11    ARM,
12    Apple,
13    Unknown(String),
14}
15
16impl fmt::Display for CPUVendor {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        match self {
19            CPUVendor::Intel => write!(f, "Intel"),
20            CPUVendor::AMD => write!(f, "AMD"),
21            CPUVendor::ARM => write!(f, "ARM"),
22            CPUVendor::Apple => write!(f, "Apple"),
23            CPUVendor::Unknown(name) => write!(f, "{name}"),
24        }
25    }
26}
27
28/// CPU feature flags
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub enum CPUFeature {
31    AVX,
32    AVX2,
33    AVX512,
34    SSE,
35    SSE2,
36    SSE3,
37    SSE41,
38    SSE42,
39    FMA,
40    AES,
41    SHA,
42    BMI1,
43    BMI2,
44    RDRAND,
45    RDSEED,
46    POPCNT,
47    LZCNT,
48    MOVBE,
49    PREFETCHWT1,
50    CLFLUSHOPT,
51    CLWB,
52    XSAVE,
53    XSAVEOPT,
54    XSAVEC,
55    XSAVES,
56    FSGSBASE,
57    RDTSCP,
58    F16C,
59    Unknown(String),
60}
61
62impl fmt::Display for CPUFeature {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            CPUFeature::AVX => write!(f, "AVX"),
66            CPUFeature::AVX2 => write!(f, "AVX2"),
67            CPUFeature::AVX512 => write!(f, "AVX512"),
68            CPUFeature::SSE => write!(f, "SSE"),
69            CPUFeature::SSE2 => write!(f, "SSE2"),
70            CPUFeature::SSE3 => write!(f, "SSE3"),
71            CPUFeature::SSE41 => write!(f, "SSE4.1"),
72            CPUFeature::SSE42 => write!(f, "SSE4.2"),
73            CPUFeature::FMA => write!(f, "FMA"),
74            CPUFeature::AES => write!(f, "AES"),
75            CPUFeature::SHA => write!(f, "SHA"),
76            CPUFeature::BMI1 => write!(f, "BMI1"),
77            CPUFeature::BMI2 => write!(f, "BMI2"),
78            CPUFeature::RDRAND => write!(f, "RDRAND"),
79            CPUFeature::RDSEED => write!(f, "RDSEED"),
80            CPUFeature::POPCNT => write!(f, "POPCNT"),
81            CPUFeature::LZCNT => write!(f, "LZCNT"),
82            CPUFeature::MOVBE => write!(f, "MOVBE"),
83            CPUFeature::PREFETCHWT1 => write!(f, "PREFETCHWT1"),
84            CPUFeature::CLFLUSHOPT => write!(f, "CLFLUSHOPT"),
85            CPUFeature::CLWB => write!(f, "CLWB"),
86            CPUFeature::XSAVE => write!(f, "XSAVE"),
87            CPUFeature::XSAVEOPT => write!(f, "XSAVEOPT"),
88            CPUFeature::XSAVEC => write!(f, "XSAVEC"),
89            CPUFeature::XSAVES => write!(f, "XSAVES"),
90            CPUFeature::FSGSBASE => write!(f, "FSGSBASE"),
91            CPUFeature::RDTSCP => write!(f, "RDTSCP"),
92            CPUFeature::F16C => write!(f, "F16C"),
93            CPUFeature::Unknown(name) => write!(f, "{name}"),
94        }
95    }
96}
97
98/// CPU information and specifications
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct CPUInfo {
101    /// CPU vendor
102    pub vendor: CPUVendor,
103    /// CPU model name
104    pub model_name: String,
105    /// CPU brand string
106    pub brand: String,
107    /// Number of physical cores
108    pub physical_cores: u32,
109    /// Number of logical cores (threads)
110    pub logical_cores: u32,
111    /// Base frequency in MHz
112    pub base_frequency: u32,
113    /// Maximum frequency in MHz
114    pub max_frequency: u32,
115    /// L1 cache size in KB
116    pub l1_cache_kb: u32,
117    /// L2 cache size in KB
118    pub l2_cache_kb: u32,
119    /// L3 cache size in KB
120    pub l3_cache_kb: u32,
121    /// CPU features supported
122    pub features: Vec<CPUFeature>,
123    /// Architecture (x86_64, arm64, etc.)
124    pub architecture: String,
125    /// CPU usage percentage per core
126    pub core_usage: Vec<f32>,
127    /// CPU temperature in Celsius (if available)
128    pub temperature: Option<f32>,
129    /// CPU power consumption in watts (if available)
130    pub power_consumption: Option<f32>,
131    /// CPU stepping
132    pub stepping: Option<u32>,
133    /// CPU family
134    pub family: Option<u32>,
135    /// CPU model number
136    pub model: Option<u32>,
137    /// CPU microcode version
138    pub microcode: Option<String>,
139    /// CPU vulnerabilities (Spectre, Meltdown, etc.)
140    pub vulnerabilities: Vec<String>,
141}
142
143impl CPUInfo {
144    /// Query CPU information from the system
145    pub fn query() -> Result<Self> {
146        let mut system = System::new_all();
147        system.refresh_all();
148
149        let cpus = system.cpus();
150        if cpus.is_empty() {
151            return Err(HardwareQueryError::system_info_unavailable(
152                "No CPU information available",
153            ));
154        }
155
156        let cpu = &cpus[0];
157        let brand = cpu.brand().to_string();
158        let vendor = Self::parse_vendor(&brand);
159
160        Ok(Self {
161            vendor,
162            model_name: Self::extract_model_name(&brand),
163            brand,
164            physical_cores: Self::detect_physical_cores()?,
165            logical_cores: system.cpus().len() as u32,
166            base_frequency: cpu.frequency() as u32,
167            max_frequency: Self::detect_max_frequency()?,
168            l1_cache_kb: Self::detect_l1_cache()?,
169            l2_cache_kb: Self::detect_l2_cache()?,
170            l3_cache_kb: Self::detect_l3_cache()?,
171            features: Self::detect_features()?,
172            architecture: Self::detect_architecture(),
173            core_usage: cpus.iter().map(|cpu| cpu.cpu_usage()).collect(),
174            temperature: Self::detect_temperature(),
175            power_consumption: Self::detect_power_consumption(),
176            stepping: Self::detect_stepping()?,
177            family: Self::detect_family()?,
178            model: Self::detect_model()?,
179            microcode: Self::detect_microcode(),
180            vulnerabilities: Self::detect_vulnerabilities()?,
181        })
182    }
183
184    /// Get CPU vendor
185    pub fn vendor(&self) -> &CPUVendor {
186        &self.vendor
187    }
188
189    /// Get CPU model name
190    pub fn model_name(&self) -> &str {
191        &self.model_name
192    }
193
194    /// Get CPU brand string
195    pub fn brand(&self) -> &str {
196        &self.brand
197    }
198
199    /// Get number of physical cores
200    pub fn physical_cores(&self) -> u32 {
201        self.physical_cores
202    }
203
204    /// Get number of logical cores (threads)
205    pub fn logical_cores(&self) -> u32 {
206        self.logical_cores
207    }
208
209    /// Get base frequency in MHz
210    pub fn base_frequency(&self) -> u32 {
211        self.base_frequency
212    }
213
214    /// Get maximum frequency in MHz
215    pub fn max_frequency(&self) -> u32 {
216        self.max_frequency
217    }
218
219    /// Get L1 cache size in KB
220    pub fn l1_cache_kb(&self) -> u32 {
221        self.l1_cache_kb
222    }
223
224    /// Get L2 cache size in KB
225    pub fn l2_cache_kb(&self) -> u32 {
226        self.l2_cache_kb
227    }
228
229    /// Get L3 cache size in KB
230    pub fn l3_cache_kb(&self) -> u32 {
231        self.l3_cache_kb
232    }
233
234    /// Get supported CPU features
235    pub fn features(&self) -> &[CPUFeature] {
236        &self.features
237    }
238
239    /// Check if CPU supports a specific feature
240    pub fn has_feature(&self, feature: &str) -> bool {
241        self.features
242            .iter()
243            .any(|f| f.to_string().to_lowercase() == feature.to_lowercase())
244    }
245
246    /// Get CPU architecture
247    pub fn architecture(&self) -> &str {
248        &self.architecture
249    }
250
251    /// Get current CPU usage per core
252    pub fn core_usage(&self) -> &[f32] {
253        &self.core_usage
254    }
255
256    /// Get CPU temperature (if available)
257    pub fn temperature(&self) -> Option<f32> {
258        self.temperature
259    }
260
261    /// Get CPU power consumption (if available)
262    pub fn power_consumption(&self) -> Option<f32> {
263        self.power_consumption
264    }
265
266    fn parse_vendor(brand: &str) -> CPUVendor {
267        let brand_lower = brand.to_lowercase();
268        if brand_lower.contains("intel") {
269            CPUVendor::Intel
270        } else if brand_lower.contains("amd") {
271            CPUVendor::AMD
272        } else if brand_lower.contains("arm") {
273            CPUVendor::ARM
274        } else if brand_lower.contains("apple") {
275            CPUVendor::Apple
276        } else {
277            CPUVendor::Unknown(brand.to_string())
278        }
279    }
280
281    fn extract_model_name(brand: &str) -> String {
282        // Extract model name from brand string
283        brand.split('@').next().unwrap_or(brand).trim().to_string()
284    }
285
286    fn detect_physical_cores() -> Result<u32> {
287        #[cfg(target_os = "windows")]
288        {
289            Self::detect_physical_cores_windows()
290        }
291        #[cfg(target_os = "linux")]
292        {
293            Self::detect_physical_cores_linux()
294        }
295        #[cfg(target_os = "macos")]
296        {
297            Self::detect_physical_cores_macos()
298        }
299        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
300        {
301            // Fallback for other platforms
302            Ok((num_cpus::get() / 2) as u32)
303        }
304    }
305
306    fn detect_max_frequency() -> Result<u32> {
307        #[cfg(target_os = "windows")]
308        {
309            Self::detect_max_frequency_windows()
310        }
311        #[cfg(target_os = "linux")]
312        {
313            Self::detect_max_frequency_linux()
314        }
315        #[cfg(target_os = "macos")]
316        {
317            Self::detect_max_frequency_macos()
318        }
319        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
320        {
321            Ok(3000) // Default fallback
322        }
323    }
324
325    fn detect_l1_cache() -> Result<u32> {
326        #[cfg(target_os = "windows")]
327        {
328            Self::detect_l1_cache_windows()
329        }
330        #[cfg(target_os = "linux")]
331        {
332            Self::detect_l1_cache_linux()
333        }
334        #[cfg(target_os = "macos")]
335        {
336            Self::detect_l1_cache_macos()
337        }
338        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
339        {
340            Ok(32) // Default fallback
341        }
342    }
343
344    fn detect_l2_cache() -> Result<u32> {
345        #[cfg(target_os = "windows")]
346        {
347            Self::detect_l2_cache_windows()
348        }
349        #[cfg(target_os = "linux")]
350        {
351            Self::detect_l2_cache_linux()
352        }
353        #[cfg(target_os = "macos")]
354        {
355            Self::detect_l2_cache_macos()
356        }
357        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
358        {
359            Ok(256) // Default fallback
360        }
361    }
362
363    fn detect_l3_cache() -> Result<u32> {
364        #[cfg(target_os = "windows")]
365        {
366            Self::detect_l3_cache_windows()
367        }
368        #[cfg(target_os = "linux")]
369        {
370            Self::detect_l3_cache_linux()
371        }
372        #[cfg(target_os = "macos")]
373        {
374            Self::detect_l3_cache_macos()
375        }
376        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
377        {
378            Ok(8192) // Default fallback
379        }
380    }
381
382    fn detect_features() -> Result<Vec<CPUFeature>> {
383        #[cfg(target_os = "windows")]
384        {
385            Self::detect_features_windows()
386        }
387        #[cfg(target_os = "linux")]
388        {
389            Self::detect_features_linux()
390        }
391        #[cfg(target_os = "macos")]
392        {
393            Self::detect_features_macos()
394        }
395        #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))]
396        {
397            Ok(vec![
398                CPUFeature::SSE,
399                CPUFeature::SSE2,
400                CPUFeature::SSE3,
401                CPUFeature::SSE41,
402                CPUFeature::SSE42,
403                CPUFeature::AVX,
404                CPUFeature::AVX2,
405            ])
406        }
407    }
408
409    fn detect_stepping() -> Result<Option<u32>> {
410        #[cfg(target_os = "linux")]
411        {
412            if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
413                for line in content.lines() {
414                    if line.starts_with("stepping") {
415                        if let Some(value) = line.split(':').nth(1) {
416                            return Ok(value.trim().parse().ok());
417                        }
418                    }
419                }
420            }
421        }
422        Ok(None)
423    }
424
425    fn detect_family() -> Result<Option<u32>> {
426        #[cfg(target_os = "linux")]
427        {
428            if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
429                for line in content.lines() {
430                    if line.starts_with("cpu family") {
431                        if let Some(value) = line.split(':').nth(1) {
432                            return Ok(value.trim().parse().ok());
433                        }
434                    }
435                }
436            }
437        }
438        Ok(None)
439    }
440
441    fn detect_model() -> Result<Option<u32>> {
442        #[cfg(target_os = "linux")]
443        {
444            if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
445                for line in content.lines() {
446                    if line.starts_with("model") && !line.starts_with("model name") {
447                        if let Some(value) = line.split(':').nth(1) {
448                            return Ok(value.trim().parse().ok());
449                        }
450                    }
451                }
452            }
453        }
454        Ok(None)
455    }
456
457    fn detect_microcode() -> Option<String> {
458        #[cfg(target_os = "linux")]
459        {
460            if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
461                for line in content.lines() {
462                    if line.starts_with("microcode") {
463                        if let Some(value) = line.split(':').nth(1) {
464                            return Some(value.trim().to_string());
465                        }
466                    }
467                }
468            }
469        }
470        None
471    }
472
473    fn detect_vulnerabilities() -> Result<Vec<String>> {
474        let vulnerabilities = Vec::new();
475
476        #[cfg(target_os = "linux")]
477        {
478            if let Ok(entries) = fs::read_dir("/sys/devices/system/cpu/vulnerabilities") {
479                for entry in entries.flatten() {
480                    if let Ok(name) = entry.file_name().into_string() {
481                        if let Ok(status) = fs::read_to_string(entry.path()) {
482                            let status = status.trim();
483                            if !status.starts_with("Not affected")
484                                && !status.starts_with("Mitigation")
485                            {
486                                vulnerabilities.push(format!("{}: {}", name, status));
487                            }
488                        }
489                    }
490                }
491            }
492        }
493
494        Ok(vulnerabilities)
495    }
496
497    // Windows-specific implementations
498    #[cfg(target_os = "windows")]
499    fn detect_physical_cores_windows() -> Result<u32> {
500        match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
501            Ok(wmi_con) => {
502                let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
503                    .raw_query("SELECT NumberOfCores FROM Win32_Processor")
504                    .map_err(|e| {
505                        HardwareQueryError::system_info_unavailable(format!(
506                            "WMI query failed: {e}"
507                        ))
508                    })?;
509
510                if let Some(result) = results.first() {
511                    if let Some(wmi::Variant::UI4(cores)) = result.get("NumberOfCores") {
512                        return Ok(*cores);
513                    }
514                }
515                Err(HardwareQueryError::system_info_unavailable(
516                    "Could not get core count from WMI",
517                ))
518            }
519            Err(_) => Ok((num_cpus::get() / 2) as u32), // Fallback
520        }
521    }
522
523    #[cfg(target_os = "windows")]
524    fn detect_max_frequency_windows() -> Result<u32> {
525        match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
526            Ok(wmi_con) => {
527                let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
528                    .raw_query("SELECT MaxClockSpeed FROM Win32_Processor")
529                    .map_err(|e| {
530                        HardwareQueryError::system_info_unavailable(format!(
531                            "WMI query failed: {e}"
532                        ))
533                    })?;
534
535                if let Some(result) = results.first() {
536                    if let Some(wmi::Variant::UI4(freq)) = result.get("MaxClockSpeed") {
537                        return Ok(*freq);
538                    }
539                }
540                Ok(3000) // Default fallback
541            }
542            Err(_) => Ok(3000), // Fallback
543        }
544    }
545
546    #[cfg(target_os = "windows")]
547    fn detect_l1_cache_windows() -> Result<u32> {
548        match wmi::WMIConnection::new(wmi::COMLibrary::new()?) {
549            Ok(wmi_con) => {
550                let results: Vec<std::collections::HashMap<String, wmi::Variant>> = wmi_con
551                    .raw_query("SELECT MaxCacheSize FROM Win32_CacheMemory WHERE Level = 3")
552                    .map_err(|e| {
553                        HardwareQueryError::system_info_unavailable(format!(
554                            "WMI query failed: {e}"
555                        ))
556                    })?;
557
558                if let Some(result) = results.first() {
559                    if let Some(wmi::Variant::UI4(size)) = result.get("MaxCacheSize") {
560                        return Ok(*size);
561                    }
562                }
563                Ok(32) // Default fallback
564            }
565            Err(_) => Ok(32), // Fallback
566        }
567    }
568
569    #[cfg(target_os = "windows")]
570    fn detect_l2_cache_windows() -> Result<u32> {
571        Ok(256) // Placeholder - would need more complex WMI query
572    }
573
574    #[cfg(target_os = "windows")]
575    fn detect_l3_cache_windows() -> Result<u32> {
576        Ok(8192) // Placeholder - would need more complex WMI query
577    }
578
579    #[cfg(target_os = "windows")]
580    fn detect_features_windows() -> Result<Vec<CPUFeature>> {
581        // Windows feature detection would use CPUID instructions
582        // This is a simplified implementation
583        Ok(vec![
584            CPUFeature::SSE,
585            CPUFeature::SSE2,
586            CPUFeature::SSE3,
587            CPUFeature::SSE41,
588            CPUFeature::SSE42,
589            CPUFeature::AVX,
590            CPUFeature::AVX2,
591        ])
592    }
593
594    // Linux-specific implementations
595    #[cfg(target_os = "linux")]
596    fn detect_physical_cores_linux() -> Result<u32> {
597        if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
598            let mut core_ids = std::collections::HashSet::new();
599            for line in content.lines() {
600                if line.starts_with("core id") {
601                    if let Some(value) = line.split(':').nth(1) {
602                        if let Ok(id) = value.trim().parse::<u32>() {
603                            core_ids.insert(id);
604                        }
605                    }
606                }
607            }
608            if !core_ids.is_empty() {
609                return Ok(core_ids.len() as u32);
610            }
611        }
612        Ok((num_cpus::get() / 2) as u32) // Fallback
613    }
614
615    #[cfg(target_os = "linux")]
616    fn detect_max_frequency_linux() -> Result<u32> {
617        if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
618            for line in content.lines() {
619                if line.starts_with("cpu MHz") {
620                    if let Some(value) = line.split(':').nth(1) {
621                        if let Ok(freq) = value.trim().parse::<f32>() {
622                            return Ok(freq as u32);
623                        }
624                    }
625                }
626            }
627        }
628
629        // Try reading from scaling_max_freq
630        if let Ok(content) =
631            fs::read_to_string("/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq")
632        {
633            if let Ok(freq) = content.trim().parse::<u32>() {
634                return Ok(freq / 1000); // Convert from kHz to MHz
635            }
636        }
637
638        Ok(3000) // Default fallback
639    }
640
641    #[cfg(target_os = "linux")]
642    fn detect_l1_cache_linux() -> Result<u32> {
643        if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index0/size") {
644            if let Ok(size_str) = content.trim().parse::<String>() {
645                if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
646                    return Ok(size);
647                }
648            }
649        }
650        Ok(32) // Default fallback
651    }
652
653    #[cfg(target_os = "linux")]
654    fn detect_l2_cache_linux() -> Result<u32> {
655        if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index1/size") {
656            if let Ok(size_str) = content.trim().parse::<String>() {
657                if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
658                    return Ok(size);
659                }
660            }
661        }
662        Ok(256) // Default fallback
663    }
664
665    #[cfg(target_os = "linux")]
666    fn detect_l3_cache_linux() -> Result<u32> {
667        if let Ok(content) = fs::read_to_string("/sys/devices/system/cpu/cpu0/cache/index2/size") {
668            if let Ok(size_str) = content.trim().parse::<String>() {
669                if let Ok(size) = size_str.trim_end_matches('K').parse::<u32>() {
670                    return Ok(size);
671                }
672            }
673        }
674        Ok(8192) // Default fallback
675    }
676
677    #[cfg(target_os = "linux")]
678    fn detect_features_linux() -> Result<Vec<CPUFeature>> {
679        let mut features = Vec::new();
680
681        if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
682            for line in content.lines() {
683                if line.starts_with("flags") {
684                    if let Some(flags_str) = line.split(':').nth(1) {
685                        let flags: Vec<&str> = flags_str.split_whitespace().collect();
686
687                        for flag in flags {
688                            match flag {
689                                "sse" => features.push(CPUFeature::SSE),
690                                "sse2" => features.push(CPUFeature::SSE2),
691                                "sse3" => features.push(CPUFeature::SSE3),
692                                "sse4_1" => features.push(CPUFeature::SSE41),
693                                "sse4_2" => features.push(CPUFeature::SSE42),
694                                "avx" => features.push(CPUFeature::AVX),
695                                "avx2" => features.push(CPUFeature::AVX2),
696                                "avx512f" => features.push(CPUFeature::AVX512),
697                                "fma" => features.push(CPUFeature::FMA),
698                                "aes" => features.push(CPUFeature::AES),
699                                "sha_ni" => features.push(CPUFeature::SHA),
700                                "bmi1" => features.push(CPUFeature::BMI1),
701                                "bmi2" => features.push(CPUFeature::BMI2),
702                                "rdrand" => features.push(CPUFeature::RDRAND),
703                                "rdseed" => features.push(CPUFeature::RDSEED),
704                                "popcnt" => features.push(CPUFeature::POPCNT),
705                                "lzcnt" => features.push(CPUFeature::LZCNT),
706                                _ => {}
707                            }
708                        }
709                        break;
710                    }
711                }
712            }
713        }
714
715        Ok(features)
716    }
717
718    // macOS-specific implementations
719    #[cfg(target_os = "macos")]
720    fn detect_physical_cores_macos() -> Result<u32> {
721        use std::process::Command;
722
723        let output = Command::new("sysctl")
724            .arg("-n")
725            .arg("hw.physicalcpu")
726            .output()
727            .map_err(|e| {
728                HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
729            })?;
730
731        if output.status.success() {
732            let cores_str = String::from_utf8_lossy(&output.stdout);
733            if let Ok(cores) = cores_str.trim().parse::<u32>() {
734                return Ok(cores);
735            }
736        }
737
738        Ok((num_cpus::get() / 2) as u32) // Fallback
739    }
740
741    #[cfg(target_os = "macos")]
742    fn detect_max_frequency_macos() -> Result<u32> {
743        use std::process::Command;
744
745        let output = Command::new("sysctl")
746            .arg("-n")
747            .arg("hw.cpufrequency_max")
748            .output()
749            .map_err(|e| {
750                HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
751            })?;
752
753        if output.status.success() {
754            let freq_str = String::from_utf8_lossy(&output.stdout);
755            if let Ok(freq) = freq_str.trim().parse::<u64>() {
756                return Ok((freq / 1_000_000) as u32); // Convert Hz to MHz
757            }
758        }
759
760        Ok(3000) // Default fallback
761    }
762
763    #[cfg(target_os = "macos")]
764    fn detect_l1_cache_macos() -> Result<u32> {
765        use std::process::Command;
766
767        let output = Command::new("sysctl")
768            .arg("-n")
769            .arg("hw.l1dcachesize")
770            .output()
771            .map_err(|e| {
772                HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
773            })?;
774
775        if output.status.success() {
776            let cache_str = String::from_utf8_lossy(&output.stdout);
777            if let Ok(cache) = cache_str.trim().parse::<u32>() {
778                return Ok(cache / 1024); // Convert bytes to KB
779            }
780        }
781
782        Ok(32) // Default fallback
783    }
784
785    #[cfg(target_os = "macos")]
786    fn detect_l2_cache_macos() -> Result<u32> {
787        use std::process::Command;
788
789        let output = Command::new("sysctl")
790            .arg("-n")
791            .arg("hw.l2cachesize")
792            .output()
793            .map_err(|e| {
794                HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
795            })?;
796
797        if output.status.success() {
798            let cache_str = String::from_utf8_lossy(&output.stdout);
799            if let Ok(cache) = cache_str.trim().parse::<u32>() {
800                return Ok(cache / 1024); // Convert bytes to KB
801            }
802        }
803
804        Ok(256) // Default fallback
805    }
806
807    #[cfg(target_os = "macos")]
808    fn detect_l3_cache_macos() -> Result<u32> {
809        use std::process::Command;
810
811        let output = Command::new("sysctl")
812            .arg("-n")
813            .arg("hw.l3cachesize")
814            .output()
815            .map_err(|e| {
816                HardwareQueryError::system_info_unavailable(format!("sysctl failed: {}", e))
817            })?;
818
819        if output.status.success() {
820            let cache_str = String::from_utf8_lossy(&output.stdout);
821            if let Ok(cache) = cache_str.trim().parse::<u32>() {
822                return Ok(cache / 1024); // Convert bytes to KB
823            }
824        }
825
826        Ok(8192) // Default fallback
827    }
828
829    #[cfg(target_os = "macos")]
830    fn detect_features_macos() -> Result<Vec<CPUFeature>> {
831        use std::process::Command;
832
833        let mut features = Vec::new();
834
835        // Check for various CPU features using sysctl
836        let feature_checks = vec![
837            ("hw.optional.sse", CPUFeature::SSE),
838            ("hw.optional.sse2", CPUFeature::SSE2),
839            ("hw.optional.sse3", CPUFeature::SSE3),
840            ("hw.optional.sse4_1", CPUFeature::SSE41),
841            ("hw.optional.sse4_2", CPUFeature::SSE42),
842            ("hw.optional.avx1_0", CPUFeature::AVX),
843            ("hw.optional.avx2_0", CPUFeature::AVX2),
844            ("hw.optional.aes", CPUFeature::AES),
845        ];
846
847        for (sysctl_name, feature) in feature_checks {
848            let output = Command::new("sysctl").arg("-n").arg(sysctl_name).output();
849
850            if let Ok(output) = output {
851                if output.status.success() {
852                    let value_str = String::from_utf8_lossy(&output.stdout);
853                    if value_str.trim() == "1" {
854                        features.push(feature);
855                    }
856                }
857            }
858        }
859
860        Ok(features)
861    }
862
863    fn detect_architecture() -> String {
864        if cfg!(target_arch = "x86_64") {
865            "x86_64".to_string()
866        } else if cfg!(target_arch = "aarch64") {
867            "aarch64".to_string()
868        } else if cfg!(target_arch = "arm") {
869            "arm".to_string()
870        } else {
871            std::env::consts::ARCH.to_string()
872        }
873    }
874
875    fn detect_temperature() -> Option<f32> {
876        // Platform-specific implementation would go here
877        None
878    }
879
880    fn detect_power_consumption() -> Option<f32> {
881        // Platform-specific implementation would go here
882        None
883    }
884}