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
33 fn path() -> &'static Path {
34 Path::new("/proc/cpuinfo")
35 }
36
37 #[cfg(test)]
38 fn from_string(raw: String) -> Self {
39 Self {raw}
40 }
41
42 pub fn read() -> io::Result<Self> {
44 Ok(Self {
45 raw: fs::read_to_string(Self::path())?
46 })
47 }
48
49 pub fn reload(&mut self) -> io::Result<()> {
51 read_to_string_mut(Self::path(), &mut self.raw)
52 }
53
54 pub fn entries<'a>(&'a self) -> impl Iterator<Item=CpuEntry<'a>> {
56 self.raw.split("\n\n")
57 .map(CpuEntry::from_str)
58 }
59
60 pub fn first<'a>(&'a self) -> Option<CpuEntry<'a>> {
62 self.entries().next()
63 }
64
65 pub fn first_value<'a>(&'a self, key: &str) -> Option<&'a str> {
67 self.first()
68 .and_then(|i| i.value(key))
69 }
70
71 pub fn unique_values<'a>(&'a self, key: &str) -> Vec<&'a str> {
73 let mut list = vec![];
74 self.entries()
75 .filter_map(|info| info.value(key))
76 .for_each(|v| {
77 if !list.contains(&v) {
78 list.push(v);
79 }
80 });
81 list
82 }
83
84 pub fn cores(&self) -> usize {
86 self.entries().count()
87 }
88
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct CpuEntry<'a> {
93 raw: &'a str
94}
95
96impl<'a> CpuEntry<'a> {
97
98 fn from_str(raw: &'a str) -> Self {
99 Self {raw}
100 }
101
102 pub fn values(&self) -> impl Iterator<Item=Option<(&'a str, &'a str)>> {
104 self.raw.split('\n')
105 .map(|line| {
106 let mut iter = line.splitn(2, ':');
108 let (key, value) = (iter.next()?, iter.next()?);
109 Some((key.trim(), value.trim()))
110 })
111 }
112
113 pub fn value(&self, key: &str) -> Option<&'a str> {
115 self.values()
116 .filter_map(|kv| kv)
117 .find_map(|(k, v)| k.eq_ignore_ascii_case(key).then(|| v))
118 }
119
120 pub fn keys(&self) -> impl Iterator<Item=&'a str> {
122 self.values()
123 .filter_map(|kv| kv)
124 .map(|(k, _)| k)
125 }
126
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 fn cpu_info() -> Cpu {
134 Cpu::from_string("\
135processor : 16
136vendor_id : AuthenticAMD
137cpu family : 23
138model : 113
139model name : AMD Ryzen 9 3900XT 12-Core Processor
140stepping : 0
141microcode : 0x8701021
142cpu MHz : 2196.035
143cache size : 512 KB
144physical id : 0
145siblings : 24
146core id : 6
147cpu cores : 12
148apicid : 13
149initial apicid : 13
150fpu : yes
151fpu_exception : yes
152cpuid level : 16
153wp : yes
154flags : 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
155bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
156bogomips : 7586.59
157TLB size : 3072 4K pages
158clflush size : 64
159cache_alignment : 64
160address sizes : 43 bits physical, 48 bits virtual
161power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]
162
163processor : 17
164vendor_id : AuthenticAMD
165cpu family : 23
166model : 113
167model name : AMD Ryzen 9 3900XT 12-Core Processor
168stepping : 0
169microcode : 0x8701021
170cpu MHz : 2196.035
171cache size : 512 KB
172physical id : 0
173siblings : 24
174core id : 6
175cpu cores : 12
176apicid : 13
177initial apicid : 13
178fpu : yes
179fpu_exception : yes
180cpuid level : 16
181wp : yes
182flags : 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
183bugs : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
184bogomips : 7586.59
185TLB size : 3072 4K pages
186clflush size : 64
187cache_alignment : 64
188address sizes : 43 bits physical, 48 bits virtual
189power management: ts ttp tm hwpstate cpb eff_freq_ro [13] [14]\n\
190 ".into())
191 }
192
193 #[test]
194 fn info_to_vec() {
195 let cpu_info = cpu_info();
196 let v: Vec<_> = cpu_info.entries().collect();
197 assert_eq!(v.len(), 2);
198 }
199
200 #[test]
201 fn info_values() {
202 let info = cpu_info();
203 let mut values = info.entries();
204 let first = values.next().unwrap();
205 println!("first {:?}", first.values().collect::<Vec<_>>());
206 let model_name = first.value("model name").unwrap();
207 assert_eq!(model_name, "AMD Ryzen 9 3900XT 12-Core Processor");
208 }
209
210 #[test]
211 fn count_cores() {
212 let cpu_info = cpu_info();
213 assert_eq!(cpu_info.cores(), 2);
214 }
215
216 #[test]
217 fn unique_values() {
218 let cpu_info = cpu_info();
219 let un = cpu_info.unique_values("model name");
220 assert_eq!(un.len(), 1);
221 }
222
223}