Skip to main content

agent_os_kernel/
command_registry.rs

1use crate::vfs::{VfsResult, VirtualFileSystem};
2use std::collections::BTreeMap;
3
4const COMMAND_STUB: &[u8] = b"#!/bin/sh\n# kernel command stub\n";
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct CommandDriver {
8    name: String,
9    commands: Vec<String>,
10}
11
12impl CommandDriver {
13    pub fn new<N, I, C>(name: N, commands: I) -> Self
14    where
15        N: Into<String>,
16        I: IntoIterator<Item = C>,
17        C: Into<String>,
18    {
19        Self {
20            name: name.into(),
21            commands: commands.into_iter().map(Into::into).collect(),
22        }
23    }
24
25    pub fn name(&self) -> &str {
26        &self.name
27    }
28
29    pub fn commands(&self) -> &[String] {
30        &self.commands
31    }
32}
33
34#[derive(Debug, Default, Clone)]
35pub struct CommandRegistry {
36    commands: BTreeMap<String, CommandDriver>,
37    warnings: Vec<String>,
38}
39
40impl CommandRegistry {
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    pub fn register(&mut self, driver: CommandDriver) {
46        for command in &driver.commands {
47            if let Some(existing) = self.commands.get(command) {
48                self.warnings.push(format!(
49                    "command \"{command}\" overridden: {} -> {}",
50                    existing.name(),
51                    driver.name()
52                ));
53            }
54
55            self.commands.insert(command.clone(), driver.clone());
56        }
57    }
58
59    pub fn warnings(&self) -> &[String] {
60        &self.warnings
61    }
62
63    pub fn resolve(&self, command: &str) -> Option<&CommandDriver> {
64        self.commands.get(command)
65    }
66
67    pub fn list(&self) -> BTreeMap<String, String> {
68        self.commands
69            .iter()
70            .map(|(command, driver)| (command.clone(), driver.name().to_owned()))
71            .collect()
72    }
73
74    pub fn populate_bin<F>(&self, vfs: &mut F) -> VfsResult<()>
75    where
76        F: VirtualFileSystem,
77    {
78        self.populate_commands(vfs, self.commands.keys())
79    }
80
81    pub fn populate_driver_bin<F>(&self, vfs: &mut F, driver: &CommandDriver) -> VfsResult<()>
82    where
83        F: VirtualFileSystem,
84    {
85        self.populate_commands(vfs, driver.commands())
86    }
87
88    fn populate_commands<F, I, S>(&self, vfs: &mut F, commands: I) -> VfsResult<()>
89    where
90        F: VirtualFileSystem,
91        I: IntoIterator<Item = S>,
92        S: AsRef<str>,
93    {
94        if !vfs.exists("/bin") {
95            vfs.mkdir("/bin", true)?;
96        }
97
98        for command in commands {
99            let path = format!("/bin/{}", command.as_ref());
100            if !vfs.exists(&path) {
101                vfs.write_file(&path, COMMAND_STUB.to_vec())?;
102                let _ = vfs.chmod(&path, 0o755);
103            }
104        }
105
106        Ok(())
107    }
108}