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