1mod extensions;
18mod hardware;
19mod implications;
20mod parsing;
21mod system;
22mod types;
23
24pub use types::{CacheInfo, ExtensionEntry, HardwareIds, RiscvInfo, SystemInfo, VectorInfo};
26
27pub use extensions::{
29 STANDARD_EXTENSIONS, S_CATEGORY_NAMES, S_EXTENSIONS, Z_CATEGORY_NAMES, Z_EXTENSIONS,
30};
31
32pub use parsing::{
34 compute_derived_extension_names, get_all_s_extensions_with_status,
35 get_all_standard_extensions_with_status, get_all_z_extensions_with_status,
36 get_extensions_with_derived, get_s_category_name, get_z_category_name, group_by_category,
37 parse_extensions_compact, parse_extensions_explained, parse_s_extensions,
38 parse_s_extensions_explained, parse_s_extensions_with_category,
39 parse_s_extensions_with_category_and_derived, parse_vector_from_isa, parse_z_extensions,
40 parse_z_extensions_explained, parse_z_extensions_with_category,
41 parse_z_extensions_with_category_and_derived, ExtensionInfo,
42};
43
44pub use hardware::{
46 get_board_info, get_cache_info, get_hardware_ids, get_hart_count, get_hart_count_num,
47 get_isa_string, get_vector_detail,
48};
49
50pub use system::{
52 get_kernel_info, get_memory_bytes, get_memory_info, get_os_info, get_uptime, get_uptime_seconds,
53};
54
55use std::fs;
56use std::process::Command;
57use sysinfo::System;
58
59#[must_use]
61pub fn is_riscv() -> bool {
62 if let Ok(output) = Command::new("uname").arg("-m").output() {
63 let arch = String::from_utf8_lossy(&output.stdout);
64 if arch.contains("riscv") {
65 return true;
66 }
67 }
68
69 if let Ok(content) = fs::read_to_string("/proc/cpuinfo") {
70 if content.contains("riscv") || content.contains("RISC-V") {
71 return true;
72 }
73 }
74
75 false
76}
77
78#[must_use]
80pub fn get_extensions_compact() -> String {
81 parse_extensions_compact(&get_isa_string())
82}
83
84#[must_use]
86pub fn get_z_extensions() -> String {
87 parse_z_extensions(&get_isa_string())
88}
89
90#[must_use]
92pub fn get_extensions_explained() -> Vec<(String, String)> {
93 parse_extensions_explained(&get_isa_string())
94}
95
96#[must_use]
98pub fn get_z_extensions_explained() -> Vec<(String, String)> {
99 parse_z_extensions_explained(&get_isa_string())
100}
101
102#[must_use]
104pub fn get_s_extensions() -> String {
105 parse_s_extensions(&get_isa_string())
106}
107
108#[must_use]
110pub fn get_s_extensions_explained() -> Vec<(String, String)> {
111 parse_s_extensions_explained(&get_isa_string())
112}
113
114#[must_use]
116pub fn get_z_extensions_with_category() -> Vec<ExtensionInfo> {
117 parse_z_extensions_with_category(&get_isa_string())
118}
119
120#[must_use]
122pub fn get_s_extensions_with_category() -> Vec<ExtensionInfo> {
123 parse_s_extensions_with_category(&get_isa_string())
124}
125
126#[must_use]
129pub fn get_extensions_with_derived_for_system() -> Vec<ExtensionInfo> {
130 get_extensions_with_derived(&get_isa_string())
131}
132
133#[must_use]
136pub fn get_z_extensions_with_category_and_derived() -> Vec<ExtensionInfo> {
137 parse_z_extensions_with_category_and_derived(&get_isa_string())
138}
139
140#[must_use]
143pub fn get_s_extensions_with_category_and_derived() -> Vec<ExtensionInfo> {
144 parse_s_extensions_with_category_and_derived(&get_isa_string())
145}
146
147#[must_use]
149pub fn collect_riscv_info() -> RiscvInfo {
150 use types::ExtensionEntry;
151
152 let mut sys = System::new();
153 sys.refresh_cpu_all();
154
155 let isa = get_isa_string();
156 let exts: Vec<ExtensionEntry> = get_extensions_with_derived(&isa)
157 .into_iter()
158 .map(|e| ExtensionEntry {
159 name: e.name,
160 description: e.description,
161 derived: e.derived,
162 })
163 .collect();
164 let z_exts: Vec<ExtensionEntry> = parse_z_extensions_with_category_and_derived(&isa)
165 .into_iter()
166 .map(|e| ExtensionEntry {
167 name: e.name,
168 description: e.description,
169 derived: e.derived,
170 })
171 .collect();
172
173 let hw_ids = get_hardware_ids();
174 let isa_lower = isa.to_lowercase();
175 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
176
177 RiscvInfo {
178 isa,
179 extensions: exts,
180 z_extensions: z_exts,
181 vector: VectorInfo {
182 enabled: base.contains('v') || isa_lower.contains("zve"),
183 vlen: None,
184 elen: None,
185 },
186 hart_count: sys.cpus().len(),
187 hardware_ids: hw_ids,
188 cache: CacheInfo::default(),
189 }
190}
191
192#[must_use]
194pub fn collect_all_info() -> SystemInfo {
195 use types::ExtensionEntry;
196
197 let mut sys = System::new();
198 sys.refresh_memory();
199 sys.refresh_cpu_all();
200
201 let isa = get_isa_string();
202 let exts: Vec<ExtensionEntry> = get_extensions_with_derived(&isa)
203 .into_iter()
204 .map(|e| ExtensionEntry {
205 name: e.name,
206 description: e.description,
207 derived: e.derived,
208 })
209 .collect();
210 let z_exts: Vec<ExtensionEntry> = parse_z_extensions_with_category_and_derived(&isa)
211 .into_iter()
212 .map(|e| ExtensionEntry {
213 name: e.name,
214 description: e.description,
215 derived: e.derived,
216 })
217 .collect();
218 let s_exts: Vec<ExtensionEntry> = parse_s_extensions_with_category_and_derived(&isa)
219 .into_iter()
220 .map(|e| ExtensionEntry {
221 name: e.name,
222 description: e.description,
223 derived: e.derived,
224 })
225 .collect();
226
227 let hw_ids = get_hardware_ids();
228 let isa_lower = isa.to_lowercase();
229 let base = isa_lower.split('_').next().unwrap_or(&isa_lower);
230
231 SystemInfo {
232 isa,
233 extensions: exts,
234 z_extensions: z_exts,
235 s_extensions: s_exts,
236 vector: VectorInfo {
237 enabled: base.contains('v') || isa_lower.contains("zve"),
238 vlen: None,
239 elen: None,
240 },
241 hart_count: sys.cpus().len(),
242 hardware_ids: hw_ids,
243 cache: CacheInfo::default(),
244 board: get_board_info(),
245 memory_used_bytes: sys.used_memory(),
246 memory_total_bytes: sys.total_memory(),
247 kernel: get_kernel_info(),
248 os: get_os_info(),
249 uptime_seconds: System::uptime(),
250 }
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256
257 #[test]
260 fn test_get_uptime() {
261 let uptime = get_uptime();
262 assert!(!uptime.is_empty());
263 }
264
265 #[test]
266 fn test_get_uptime_seconds() {
267 let secs = get_uptime_seconds();
268 assert!(secs > 0);
269 }
270
271 #[test]
272 fn test_get_memory_bytes() {
273 let (used, total) = get_memory_bytes();
274 assert!(total > 0);
275 assert!(used <= total);
276 }
277
278 #[test]
279 fn test_get_kernel_info() {
280 let kernel = get_kernel_info();
281 assert!(!kernel.is_empty());
282 }
283
284 #[test]
285 fn test_get_os_info() {
286 let os = get_os_info();
287 assert!(!os.is_empty());
288 }
289
290 #[test]
291 fn test_hardware_ids_default() {
292 let ids = HardwareIds::default();
293 assert!(ids.mvendorid.is_empty());
294 assert!(ids.marchid.is_empty());
295 assert!(ids.mimpid.is_empty());
296 }
297
298 #[cfg(target_arch = "riscv64")]
301 mod riscv_hardware_tests {
302 use super::*;
303
304 #[test]
305 fn hw_is_riscv() {
306 assert!(is_riscv());
307 }
308
309 #[test]
310 fn hw_isa_string_valid() {
311 let isa = get_isa_string();
312 assert!(isa.starts_with("rv64") || isa.starts_with("rv32"));
313 }
314
315 #[test]
316 fn hw_isa_string_has_base() {
317 let isa = get_isa_string();
318 assert!(isa.contains('i') || isa.contains('e'));
319 }
320
321 #[test]
322 fn hw_extensions_not_empty() {
323 let ext = get_extensions_compact();
324 assert!(!ext.is_empty());
325 assert!(ext.contains('I') || ext.contains('E'));
326 }
327
328 #[test]
329 fn hw_hart_count_positive() {
330 let hart_str = get_hart_count();
331 assert!(hart_str.contains("hart"));
332 let num: String = hart_str
333 .chars()
334 .take_while(|c| c.is_ascii_digit())
335 .collect();
336 let count: usize = num.parse().unwrap_or(0);
337 assert!(count > 0);
338 }
339
340 #[test]
341 fn hw_hardware_ids_present() {
342 let ids = get_hardware_ids();
343 let has_any =
344 !ids.mvendorid.is_empty() || !ids.marchid.is_empty() || !ids.mimpid.is_empty();
345 assert!(has_any);
346 }
347
348 #[test]
349 fn hw_collect_all_info() {
350 let info = collect_all_info();
351 assert!(!info.isa.is_empty());
352 assert!(!info.extensions.is_empty());
353 assert!(info.hart_count > 0);
354 }
355
356 #[test]
357 fn hw_collect_riscv_info() {
358 let info = collect_riscv_info();
359 assert!(!info.isa.is_empty());
360 assert!(!info.extensions.is_empty());
361 assert!(info.hart_count > 0);
362 }
363
364 #[test]
365 fn hw_riscv_info_excludes_system_fields() {
366 let riscv_info = collect_riscv_info();
367 let all_info = collect_all_info();
368
369 assert_eq!(riscv_info.isa, all_info.isa);
371 assert_eq!(riscv_info.extensions, all_info.extensions);
372 assert_eq!(riscv_info.z_extensions, all_info.z_extensions);
373 assert_eq!(riscv_info.hart_count, all_info.hart_count);
374
375 }
379 }
380}