1#![no_std]
49#![deny(missing_docs)]
50#![deny(clippy::all)]
51#![warn(clippy::pedantic)]
52#![allow(clippy::module_name_repetitions)]
53
54extern crate alloc;
55
56pub mod commands;
57mod parser;
58
59pub use parser::{Command, ParseError, Parser};
60
61use alloc::string::String;
62use alloc::vec::Vec;
63use alloc::format;
64
65#[derive(Debug, Clone)]
67pub struct ShellConfig {
68 pub max_history: usize,
70 pub echo: bool,
72 pub prompt: &'static str,
74}
75
76impl Default for ShellConfig {
77 fn default() -> Self {
78 Self {
79 max_history: 32,
80 echo: true,
81 prompt: "ruvix> ",
82 }
83 }
84}
85
86#[derive(Debug, Clone, Default)]
88pub struct MemoryStats {
89 pub total_bytes: u64,
91 pub used_bytes: u64,
93 pub free_bytes: u64,
95 pub region_count: u32,
97 pub slab_count: u32,
99 pub peak_bytes: u64,
101}
102
103#[derive(Debug, Clone)]
105pub struct TaskInfo {
106 pub id: u32,
108 pub name: [u8; 16],
110 pub state: TaskState,
112 pub priority: u8,
114 pub partition: u8,
116 pub cpu_affinity: u8,
118 pub cap_count: u16,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124#[repr(u8)]
125pub enum TaskState {
126 Ready = 0,
128 Running = 1,
130 Blocked = 2,
132 Sleeping = 3,
134 Exited = 4,
136}
137
138#[derive(Debug, Clone)]
140pub struct CpuInfo {
141 pub id: u8,
143 pub online: bool,
145 pub is_primary: bool,
147 pub freq_mhz: u32,
149 pub load_percent: u8,
151}
152
153#[derive(Debug, Clone, Default)]
155pub struct QueueStats {
156 pub queue_count: u32,
158 pub pending_messages: u64,
160 pub messages_sent: u64,
162 pub messages_received: u64,
164 pub zero_copy_count: u64,
166}
167
168#[derive(Debug, Clone, Default)]
170pub struct VectorStats {
171 pub store_count: u32,
173 pub vector_count: u64,
175 pub total_dimensions: u32,
177 pub memory_bytes: u64,
179 pub reads: u64,
181 pub writes: u64,
183}
184
185#[derive(Debug, Clone, Default)]
187pub struct ProofStats {
188 pub generated: u64,
190 pub verified: u64,
192 pub rejected: u64,
194 pub cache_entries: u32,
196 pub cache_hits: u64,
198 pub cache_misses: u64,
200 pub tier0_count: u64,
202 pub tier1_count: u64,
204 pub tier2_count: u64,
206}
207
208#[derive(Debug, Clone)]
210pub struct CapEntry {
211 pub handle: u32,
213 pub object_id: u64,
215 pub object_type: u8,
217 pub rights: u32,
219 pub badge: u64,
221 pub depth: u8,
223}
224
225#[derive(Debug, Clone)]
227pub struct WitnessEntry {
228 pub seq: u64,
230 pub timestamp_ns: u64,
232 pub operation: u8,
234 pub object_id: u64,
236 pub hash_prefix: [u8; 8],
238}
239
240#[derive(Debug, Clone, Default)]
242pub struct PerfCounters {
243 pub syscalls: u64,
245 pub context_switches: u64,
247 pub interrupts: u64,
249 pub page_faults: u64,
251 pub ipi_sent: u64,
253 pub cpu_cycles: u64,
255}
256
257#[derive(Debug, Clone)]
259pub struct KernelInfo {
260 pub version: &'static str,
262 pub build_time: &'static str,
264 pub boot_time_ns: u64,
266 pub current_time_ns: u64,
268 pub cpu_count: u8,
270}
271
272pub trait ShellBackend {
276 fn kernel_info(&self) -> KernelInfo;
278
279 fn memory_stats(&self) -> MemoryStats;
281
282 fn task_list(&self) -> Vec<TaskInfo>;
284
285 fn cpu_info(&self) -> Vec<CpuInfo>;
287
288 fn queue_stats(&self) -> QueueStats;
290
291 fn vector_stats(&self) -> VectorStats;
293
294 fn proof_stats(&self) -> ProofStats;
296
297 fn capability_entries(&self, task_id: Option<u32>) -> Vec<CapEntry>;
299
300 fn witness_entries(&self, count: usize) -> Vec<WitnessEntry>;
302
303 fn perf_counters(&self) -> PerfCounters;
305
306 fn trace_enabled(&self) -> bool;
308
309 fn set_trace(&mut self, enabled: bool);
311
312 fn reboot(&mut self);
314}
315
316#[derive(Debug)]
318pub struct Shell {
319 config: ShellConfig,
320 parser: Parser,
321 history: Vec<String>,
322}
323
324impl Shell {
325 #[must_use]
327 pub fn new(config: ShellConfig) -> Self {
328 Self {
329 config,
330 parser: Parser::new(),
331 history: Vec::new(),
332 }
333 }
334
335 #[must_use]
337 pub fn default_shell() -> Self {
338 Self::new(ShellConfig::default())
339 }
340
341 #[must_use]
343 pub fn prompt(&self) -> &'static str {
344 self.config.prompt
345 }
346
347 pub fn execute_line<B: ShellBackend>(&mut self, line: &str, backend: &mut B) -> String {
349 let trimmed = line.trim();
350
351 if trimmed.is_empty() {
352 return String::new();
353 }
354
355 if self.history.len() >= self.config.max_history {
357 self.history.remove(0);
358 }
359 self.history.push(String::from(trimmed));
360
361 match self.parser.parse(trimmed) {
363 Ok(cmd) => self.dispatch_command(cmd, backend),
364 Err(e) => format!("Error: {}\nType 'help' for available commands.", e),
365 }
366 }
367
368 fn dispatch_command<B: ShellBackend>(&self, cmd: Command, backend: &mut B) -> String {
370 match cmd {
371 Command::Help => commands::help::execute(),
372 Command::Info => commands::info::execute(backend),
373 Command::Mem => commands::mem::execute(backend),
374 Command::Tasks => commands::tasks::execute(backend),
375 Command::Caps { task_id } => commands::caps::execute(backend, task_id),
376 Command::Queues => commands::queues::execute(backend),
377 Command::Vectors => commands::vectors::execute(backend),
378 Command::Proofs => commands::proofs::execute(backend),
379 Command::Cpu => commands::cpu::execute(backend),
380 Command::Witness { count } => commands::witness::execute(backend, count),
381 Command::Perf => commands::perf::execute(backend),
382 Command::Trace { enable } => {
383 if let Some(on) = enable {
384 backend.set_trace(on);
385 if on {
386 String::from("Syscall tracing enabled.")
387 } else {
388 String::from("Syscall tracing disabled.")
389 }
390 } else {
391 if backend.trace_enabled() {
392 String::from("Syscall tracing: ENABLED")
393 } else {
394 String::from("Syscall tracing: DISABLED")
395 }
396 }
397 }
398 Command::Reboot => {
399 backend.reboot();
400 String::from("Rebooting...")
401 }
402 }
403 }
404
405 #[must_use]
407 pub fn history(&self) -> &[String] {
408 &self.history
409 }
410
411 pub fn clear_history(&mut self) {
413 self.history.clear();
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420 use alloc::vec;
421
422 struct MockBackend {
424 trace_on: bool,
425 rebooted: bool,
426 }
427
428 impl MockBackend {
429 fn new() -> Self {
430 Self {
431 trace_on: false,
432 rebooted: false,
433 }
434 }
435 }
436
437 impl ShellBackend for MockBackend {
438 fn kernel_info(&self) -> KernelInfo {
439 KernelInfo {
440 version: "0.1.0-test",
441 build_time: "2024-01-01T00:00:00Z",
442 boot_time_ns: 1_000_000_000,
443 current_time_ns: 2_000_000_000,
444 cpu_count: 4,
445 }
446 }
447
448 fn memory_stats(&self) -> MemoryStats {
449 MemoryStats {
450 total_bytes: 1024 * 1024 * 1024, used_bytes: 256 * 1024 * 1024, free_bytes: 768 * 1024 * 1024, region_count: 42,
454 slab_count: 128,
455 peak_bytes: 512 * 1024 * 1024,
456 }
457 }
458
459 fn task_list(&self) -> Vec<TaskInfo> {
460 vec![
461 TaskInfo {
462 id: 0,
463 name: *b"idle\0\0\0\0\0\0\0\0\0\0\0\0",
464 state: TaskState::Running,
465 priority: 0,
466 partition: 0,
467 cpu_affinity: 0xFF,
468 cap_count: 4,
469 },
470 TaskInfo {
471 id: 1,
472 name: *b"init\0\0\0\0\0\0\0\0\0\0\0\0",
473 state: TaskState::Ready,
474 priority: 100,
475 partition: 0,
476 cpu_affinity: 0xFF,
477 cap_count: 16,
478 },
479 ]
480 }
481
482 fn cpu_info(&self) -> Vec<CpuInfo> {
483 vec![
484 CpuInfo {
485 id: 0,
486 online: true,
487 is_primary: true,
488 freq_mhz: 1800,
489 load_percent: 25,
490 },
491 ]
492 }
493
494 fn queue_stats(&self) -> QueueStats {
495 QueueStats {
496 queue_count: 8,
497 pending_messages: 12,
498 messages_sent: 1000,
499 messages_received: 988,
500 zero_copy_count: 500,
501 }
502 }
503
504 fn vector_stats(&self) -> VectorStats {
505 VectorStats {
506 store_count: 2,
507 vector_count: 10000,
508 total_dimensions: 768,
509 memory_bytes: 30 * 1024 * 1024,
510 reads: 50000,
511 writes: 10000,
512 }
513 }
514
515 fn proof_stats(&self) -> ProofStats {
516 ProofStats {
517 generated: 15000,
518 verified: 14950,
519 rejected: 50,
520 cache_entries: 64,
521 cache_hits: 12000,
522 cache_misses: 3000,
523 tier0_count: 10000,
524 tier1_count: 4500,
525 tier2_count: 500,
526 }
527 }
528
529 fn capability_entries(&self, _task_id: Option<u32>) -> Vec<CapEntry> {
530 vec![
531 CapEntry {
532 handle: 0,
533 object_id: 0x1000,
534 object_type: 1,
535 rights: 0x07,
536 badge: 0,
537 depth: 0,
538 },
539 ]
540 }
541
542 fn witness_entries(&self, count: usize) -> Vec<WitnessEntry> {
543 (0..count.min(5))
544 .map(|i| WitnessEntry {
545 seq: i as u64,
546 timestamp_ns: 1_000_000_000 + i as u64 * 1000,
547 operation: 1,
548 object_id: 0x1000 + i as u64,
549 hash_prefix: [0xAB; 8],
550 })
551 .collect()
552 }
553
554 fn perf_counters(&self) -> PerfCounters {
555 PerfCounters {
556 syscalls: 100000,
557 context_switches: 5000,
558 interrupts: 250000,
559 page_faults: 100,
560 ipi_sent: 500,
561 cpu_cycles: 1_000_000_000_000,
562 }
563 }
564
565 fn trace_enabled(&self) -> bool {
566 self.trace_on
567 }
568
569 fn set_trace(&mut self, enabled: bool) {
570 self.trace_on = enabled;
571 }
572
573 fn reboot(&mut self) {
574 self.rebooted = true;
575 }
576 }
577
578 #[test]
579 fn test_shell_creation() {
580 let shell = Shell::default_shell();
581 assert_eq!(shell.prompt(), "ruvix> ");
582 }
583
584 #[test]
585 fn test_help_command() {
586 let mut shell = Shell::default_shell();
587 let mut backend = MockBackend::new();
588 let output = shell.execute_line("help", &mut backend);
589 assert!(output.contains("help"));
590 assert!(output.contains("info"));
591 assert!(output.contains("mem"));
592 }
593
594 #[test]
595 fn test_info_command() {
596 let mut shell = Shell::default_shell();
597 let mut backend = MockBackend::new();
598 let output = shell.execute_line("info", &mut backend);
599 assert!(output.contains("0.1.0-test"));
600 assert!(output.contains("CPU"));
601 }
602
603 #[test]
604 fn test_trace_toggle() {
605 let mut shell = Shell::default_shell();
606 let mut backend = MockBackend::new();
607
608 let output = shell.execute_line("trace", &mut backend);
610 assert!(output.contains("DISABLED"));
611
612 let output = shell.execute_line("trace on", &mut backend);
614 assert!(output.contains("enabled"));
615 assert!(backend.trace_enabled());
616
617 let output = shell.execute_line("trace off", &mut backend);
619 assert!(output.contains("disabled"));
620 assert!(!backend.trace_enabled());
621 }
622
623 #[test]
624 fn test_unknown_command() {
625 let mut shell = Shell::default_shell();
626 let mut backend = MockBackend::new();
627 let output = shell.execute_line("unknown_cmd", &mut backend);
628 assert!(output.contains("Error"));
629 }
630
631 #[test]
632 fn test_history() {
633 let mut shell = Shell::default_shell();
634 let mut backend = MockBackend::new();
635
636 shell.execute_line("help", &mut backend);
637 shell.execute_line("info", &mut backend);
638
639 let history = shell.history();
640 assert_eq!(history.len(), 2);
641 assert_eq!(history[0], "help");
642 assert_eq!(history[1], "info");
643 }
644
645 #[test]
646 fn test_empty_line() {
647 let mut shell = Shell::default_shell();
648 let mut backend = MockBackend::new();
649 let output = shell.execute_line("", &mut backend);
650 assert!(output.is_empty());
651
652 let output = shell.execute_line(" ", &mut backend);
653 assert!(output.is_empty());
654 }
655}