1use crate::util::read_to_string_mut;
21
22use std::path::Path;
23use std::{fs, io};
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct Cpu {
28 raw: String,
29}
30
31impl Cpu {
32 fn path() -> &'static Path {
33 Path::new("/proc/cpuinfo")
34 }
35
36 #[cfg(test)]
37 fn from_string(raw: String) -> Self {
38 Self { raw }
39 }
40
41 pub fn read() -> io::Result<Self> {
43 Ok(Self {
44 raw: fs::read_to_string(Self::path())?,
45 })
46 }
47
48 pub fn reload(&mut self) -> io::Result<()> {
50 read_to_string_mut(Self::path(), &mut self.raw)
51 }
52
53 pub fn entries<'a>(&'a self) -> impl Iterator<Item = CpuEntry<'a>> {
55 self.raw.split("\n\n").map(CpuEntry::from_str)
56 }
57
58 pub fn first<'a>(&'a self) -> Option<CpuEntry<'a>> {
60 self.entries().next()
61 }
62
63 pub fn first_value<'a>(&'a self, key: &str) -> Option<&'a str> {
65 self.first().and_then(|i| i.value(key))
66 }
67
68 pub fn unique_values<'a>(&'a self, key: &str) -> Vec<&'a str> {
70 let mut list = vec![];
71 self.entries()
72 .filter_map(|info| info.value(key))
73 .for_each(|v| {
74 if !list.contains(&v) {
75 list.push(v);
76 }
77 });
78 list
79 }
80
81 pub fn cores(&self) -> usize {
83 self.entries().count()
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct CpuEntry<'a> {
89 raw: &'a str,
90}
91
92impl<'a> CpuEntry<'a> {
93 fn from_str(raw: &'a str) -> Self {
94 Self { raw }
95 }
96
97 pub fn values(&self) -> impl Iterator<Item = Option<(&'a str, &'a str)>> {
99 self.raw.split('\n').map(|line| {
100 let mut iter = line.splitn(2, ':');
102 let (key, value) = (iter.next()?, iter.next()?);
103 Some((key.trim(), value.trim()))
104 })
105 }
106
107 pub fn value(&self, key: &str) -> Option<&'a str> {
109 self.values()
110 .filter_map(|kv| kv)
111 .find_map(|(k, v)| k.eq_ignore_ascii_case(key).then(|| v))
112 }
113
114 pub fn keys(&self) -> impl Iterator<Item = &'a str> {
116 self.values().filter_map(|kv| kv).map(|(k, _)| k)
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 fn cpu_info() -> Cpu {
125 Cpu::from_string("\
126processor : 16
127vendor_id : AuthenticAMD
128cpu family : 23
129model : 113
130model name : AMD Ryzen 9 3900XT 12-Core Processor
131stepping : 0
132microcode : 0x8701021
133cpu MHz : 2196.035
134cache size : 512 KB
135physical id : 0
136siblings : 24
137core id : 6
138cpu cores : 12
139apicid : 13
140initial apicid : 13
141fpu : yes
142fpu_exception : yes
143cpuid level : 16
144wp : yes
145flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate sme ssbd mba sev ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif umip rdpid overflow_recov succor smca
146bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
147bogomips : 7586.59
148TLB size : 3072 4K pages
149clflush size : 64
150cache_alignment : 64
151address sizes : 43 bits physical, 48 bits virtual
152power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]
153
154processor : 17
155vendor_id : AuthenticAMD
156cpu family : 23
157model : 113
158model name : AMD Ryzen 9 3900XT 12-Core Processor
159stepping : 0
160microcode : 0x8701021
161cpu MHz : 2196.035
162cache size : 512 KB
163physical id : 0
164siblings : 24
165core id : 6
166cpu cores : 12
167apicid : 13
168initial apicid : 13
169fpu : yes
170fpu_exception : yes
171cpuid level : 16
172wp : yes
173flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb cat_l3 cdp_l3 hw_pstate sme ssbd mba sev ibpb stibp vmmcall fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 xsaves cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local clzero irperf xsaveerptr rdpru wbnoinvd arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif umip rdpid overflow_recov succor smca
174bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
175bogomips : 7586.59
176TLB size : 3072 4K pages
177clflush size : 64
178cache_alignment : 64
179address sizes : 43 bits physical, 48 bits virtual
180power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]\n\
181 ".into())
182 }
183
184 #[test]
185 fn info_to_vec() {
186 let cpu_info = cpu_info();
187 let v: Vec<_> = cpu_info.entries().collect();
188 assert_eq!(v.len(), 2);
189 }
190
191 #[test]
192 fn info_values() {
193 let info = cpu_info();
194 let mut values = info.entries();
195 let first = values.next().unwrap();
196 println!("first {:?}", first.values().collect::<Vec<_>>());
197 let model_name = first.value("model name").unwrap();
198 assert_eq!(model_name, "AMD Ryzen 9 3900XT 12-Core Processor");
199 }
200
201 #[test]
202 fn count_cores() {
203 let cpu_info = cpu_info();
204 assert_eq!(cpu_info.cores(), 2);
205 }
206
207 #[test]
208 fn unique_values() {
209 let cpu_info = cpu_info();
210 let un = cpu_info.unique_values("model name");
211 assert_eq!(un.len(), 1);
212 }
213}