1use crate::cpu::uarch::Microarch;
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[derive(Debug, thiserror::Error)]
13pub enum CpuError {
14 #[error("Failed to detect CPU vendor: {0}")]
15 VendorDetection(String),
16 #[error("Failed to read CPU information: {0}")]
17 InfoRead(String),
18 #[error("Unsupported CPU architecture")]
19 UnsupportedArch,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub enum Vendor {
25 Intel,
26 AMD,
27 ARM,
28 Apple,
29 Unknown,
30}
31
32impl fmt::Display for Vendor {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 Vendor::Intel => write!(f, "Intel"),
36 Vendor::AMD => write!(f, "AMD"),
37 Vendor::ARM => write!(f, "ARM"),
38 Vendor::Apple => write!(f, "Apple"),
39 Vendor::Unknown => write!(f, "Unknown"),
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
46pub struct Frequency {
47 pub base: Option<f64>,
49 pub max: Option<f64>,
51 pub current: Option<f64>,
53}
54
55impl fmt::Display for Frequency {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 let base = self
58 .base
59 .map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
60 let current = self
61 .current
62 .map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
63 let max = self
64 .max
65 .map_or_else(|| "Unknown".to_string(), |v| format!("{v:.2} MHz"));
66
67 write!(f, "Base: {base}, Current: {current}, Max: {max}")
68 }
69}
70
71#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
73pub struct Version {
74 pub family: u8,
76 pub model: u8,
78 pub stepping: u8,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct CpuInfo {
85 pub vendor: Vendor,
87 pub brand_string: String,
89 pub version: Version,
91 pub physical_cores: u32,
93 pub logical_cores: u32,
95 pub frequency: Frequency,
97 pub cache_sizes: [Option<u32>; 4],
99 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
101 pub features: crate::cpu::X86Features,
102 #[cfg(target_arch = "aarch64")]
103 pub features: crate::cpu::ArmFeatures,
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub microarch: Option<Microarch>,
107 #[serde(skip_serializing_if = "Option::is_none")]
109 pub hypervisor: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
112 pub peak_flops: Option<f64>,
113 #[serde(skip_serializing_if = "Option::is_none")]
115 pub p_cores: Option<u32>,
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub e_cores: Option<u32>,
119}
120
121impl CpuInfo {
122 pub fn new() -> Result<Self, CpuError> {
128 #[cfg(target_arch = "x86_64")]
129 {
130 crate::arch::x86_64::detect_cpu()
131 }
132 #[cfg(target_arch = "aarch64")]
133 {
134 crate::arch::aarch64::detect_cpu()
135 }
136 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
137 {
138 Err(CpuError::UnsupportedArch)
139 }
140 }
141
142 #[must_use]
147 pub fn get() -> &'static Self {
148 static CPU_INFO: std::sync::LazyLock<CpuInfo> =
149 std::sync::LazyLock::new(|| CpuInfo::new().expect("Failed to detect CPU information"));
150 &CPU_INFO
151 }
152}
153
154impl Default for CpuInfo {
155 fn default() -> Self {
156 Self {
157 vendor: Vendor::Unknown,
158 brand_string: String::new(),
159 version: Version::default(),
160 physical_cores: 0,
161 logical_cores: 0,
162 frequency: Frequency::default(),
163 cache_sizes: [None; 4],
164 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
165 features: crate::cpu::X86Features::empty(),
166 #[cfg(target_arch = "aarch64")]
167 features: crate::cpu::ArmFeatures::empty(),
168 microarch: None,
169 hypervisor: None,
170 peak_flops: None,
171 p_cores: None,
172 e_cores: None,
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_vendor_display() {
183 assert_eq!(Vendor::Intel.to_string(), "Intel");
184 assert_eq!(Vendor::AMD.to_string(), "AMD");
185 assert_eq!(Vendor::ARM.to_string(), "ARM");
186 assert_eq!(Vendor::Unknown.to_string(), "Unknown");
187 }
188
189 #[test]
190 fn test_cpu_info_default() {
191 let info = CpuInfo::default();
192 assert_eq!(info.vendor, Vendor::Unknown);
193 assert_eq!(info.physical_cores, 0);
194 assert_eq!(info.logical_cores, 0);
195 assert_eq!(info.cache_sizes, [None; 4]);
196 }
197}