Skip to main content

ahrightos_fs/
component.rs

1//! Component metadata registry for AhRightOS.
2//!
3//! Commercial deployments need a stable way to answer a simple question:
4//! "which platform capabilities are present, and which interfaces may users
5//! rely on?"  This module keeps that answer data-driven and independent from
6//! syscall code.  The registry is intentionally small, static, and `no_std`
7//! friendly; future loaders can replace the static slice with signed component
8//! manifests without changing the public inspection API.
9
10use alloc::string::String;
11use alloc::vec::Vec;
12use core::fmt::Write;
13
14/// Coarse placement of a component in the AhRightOS product architecture.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum ComponentLayer {
17    Core,
18    Filesystem,
19    Device,
20    Observability,
21    Platform,
22}
23
24impl ComponentLayer {
25    pub const fn as_str(self) -> &'static str {
26        match self {
27            ComponentLayer::Core => "core",
28            ComponentLayer::Filesystem => "filesystem",
29            ComponentLayer::Device => "device",
30            ComponentLayer::Observability => "observability",
31            ComponentLayer::Platform => "platform",
32        }
33    }
34}
35
36/// Public contract stability for a component.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum ComponentStability {
39    /// Intended to remain backward compatible for users of the public ABI.
40    Stable,
41    /// Usable for experiments, but not a long-term compatibility promise.
42    Experimental,
43    /// Implementation detail; do not rely on it from applications or labs.
44    Internal,
45}
46
47impl ComponentStability {
48    pub const fn as_str(self) -> &'static str {
49        match self {
50            ComponentStability::Stable => "stable",
51            ComponentStability::Experimental => "experimental",
52            ComponentStability::Internal => "internal",
53        }
54    }
55}
56
57/// Static manifest for one AhRightOS component.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct ComponentManifest {
60    pub id: &'static str,
61    pub version: &'static str,
62    pub layer: ComponentLayer,
63    pub stability: ComponentStability,
64    pub description: &'static str,
65    pub capabilities: &'static [&'static str],
66    pub requires: &'static [&'static str],
67    pub provides: &'static [&'static str],
68}
69
70impl ComponentManifest {
71    pub const fn is_stable_public_interface(&self) -> bool {
72        matches!(self.stability, ComponentStability::Stable)
73    }
74}
75
76/// Registry validation failure.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum ComponentRegistryError {
79    MissingDependency {
80        component: &'static str,
81        dependency: &'static str,
82    },
83}
84
85/// Read-only component registry.
86#[derive(Debug)]
87pub struct ComponentRegistry {
88    components: &'static [ComponentManifest],
89}
90
91impl ComponentRegistry {
92    pub const fn new(components: &'static [ComponentManifest]) -> Self {
93        Self { components }
94    }
95
96    pub const fn components(&self) -> &'static [ComponentManifest] {
97        self.components
98    }
99
100    pub const fn len(&self) -> usize {
101        self.components.len()
102    }
103
104    pub const fn is_empty(&self) -> bool {
105        self.components.is_empty()
106    }
107
108    pub fn find(&self, id: &str) -> Option<&'static ComponentManifest> {
109        self.components.iter().find(|component| component.id == id)
110    }
111
112    pub fn validate_dependencies(&self) -> Result<(), ComponentRegistryError> {
113        for component in self.components.iter() {
114            for dependency in component.requires.iter() {
115                if self.find(dependency).is_none() {
116                    return Err(ComponentRegistryError::MissingDependency {
117                        component: component.id,
118                        dependency,
119                    });
120                }
121            }
122        }
123        Ok(())
124    }
125
126    /// Render a compact ASCII listing suitable for `/sys/fs/ahrightos/components`.
127    pub fn render_sysfs_listing(&self) -> Vec<u8> {
128        let mut out = String::from("id version layer stability capabilities requires provides\n");
129        for component in self.components.iter() {
130            let _ = write!(
131                out,
132                "{} {} {} {} ",
133                component.id,
134                component.version,
135                component.layer.as_str(),
136                component.stability.as_str()
137            );
138            write_csv(&mut out, component.capabilities);
139            out.push(' ');
140            write_csv(&mut out, component.requires);
141            out.push(' ');
142            write_csv(&mut out, component.provides);
143            out.push('\n');
144        }
145        out.into_bytes()
146    }
147}
148
149fn write_csv(out: &mut String, values: &[&str]) {
150    if values.is_empty() {
151        out.push('-');
152        return;
153    }
154
155    for (idx, value) in values.iter().enumerate() {
156        if idx > 0 {
157            out.push(',');
158        }
159        out.push_str(value);
160    }
161}
162
163const VERSION: &str = env!("CARGO_PKG_VERSION");
164
165const BUILTIN_COMPONENTS: &[ComponentManifest] = &[
166    ComponentManifest {
167        id: "ahrightos-core",
168        version: VERSION,
169        layer: ComponentLayer::Core,
170        stability: ComponentStability::Stable,
171        description: "Kernel bootstrap, heap initialization, and base runtime contracts.",
172        capabilities: &["kernel.boot", "heap.init", "runtime.base"],
173        requires: &[],
174        provides: &["abi.ahrightos-core.v0"],
175    },
176    ComponentManifest {
177        id: "ahrightos-vfs",
178        version: VERSION,
179        layer: ComponentLayer::Filesystem,
180        stability: ComponentStability::Stable,
181        description: "Unified path resolution and mount dispatch for filesystem components.",
182        capabilities: &[
183            "vfs.mount-table",
184            "vfs.path-resolution",
185            "vfs.file-dispatch",
186        ],
187        requires: &["ahrightos-core"],
188        provides: &["api.vfs.v0"],
189    },
190    ComponentManifest {
191        id: "ahrightos-ramfs",
192        version: VERSION,
193        layer: ComponentLayer::Filesystem,
194        stability: ComponentStability::Stable,
195        description: "Volatile in-memory filesystem used for root paths and /run.",
196        capabilities: &["fs.ramfs", "fs.volatile-runtime"],
197        requires: &["ahrightos-vfs"],
198        provides: &["/run", "fs.ramfs.v0"],
199    },
200    ComponentManifest {
201        id: "ahrightos-blkfs",
202        version: VERSION,
203        layer: ComponentLayer::Filesystem,
204        stability: ComponentStability::Experimental,
205        description: "Block-backed filesystem for persistent teaching experiments.",
206        capabilities: &["fs.blkfs", "block-cache", "mount.blkfs"],
207        requires: &["ahrightos-vfs"],
208        provides: &["fs.blkfs.v0"],
209    },
210    ComponentManifest {
211        id: "ahrightos-procfs",
212        version: VERSION,
213        layer: ComponentLayer::Observability,
214        stability: ComponentStability::Experimental,
215        description: "Linux-inspired read-only process/system information pseudo filesystem.",
216        capabilities: &["proc.version", "proc.meminfo", "proc.mounts"],
217        requires: &["ahrightos-vfs"],
218        provides: &["/proc/version", "/proc/meminfo", "/proc/mounts"],
219    },
220    ComponentManifest {
221        id: "ahrightos-sysfs",
222        version: VERSION,
223        layer: ComponentLayer::Observability,
224        stability: ComponentStability::Experimental,
225        description: "Linux-inspired system attribute pseudo filesystem.",
226        capabilities: &["sys.kernel", "sys.component-inspection"],
227        requires: &["ahrightos-vfs"],
228        provides: &["/sys/kernel/name", "/sys/fs/ahrightos/components"],
229    },
230    ComponentManifest {
231        id: "ahrightos-devtmpfs",
232        version: VERSION,
233        layer: ComponentLayer::Device,
234        stability: ComponentStability::Experimental,
235        description: "Device-node entry point for built-in teaching character devices.",
236        capabilities: &["dev.null", "dev.zero"],
237        requires: &["ahrightos-vfs"],
238        provides: &["/dev/null", "/dev/zero"],
239    },
240    ComponentManifest {
241        id: "ahrightos-component-platform",
242        version: VERSION,
243        layer: ComponentLayer::Platform,
244        stability: ComponentStability::Experimental,
245        description: "Manifest and registry foundation for commercial extensions.",
246        capabilities: &[
247            "component.registry",
248            "component.manifest.v0",
249            "component.sysfs-export",
250        ],
251        requires: &["ahrightos-sysfs"],
252        provides: &["/sys/fs/ahrightos/components", "component.manifest.v0"],
253    },
254];
255
256static BUILTIN_COMPONENT_REGISTRY: ComponentRegistry = ComponentRegistry::new(BUILTIN_COMPONENTS);
257
258pub fn builtin_component_registry() -> &'static ComponentRegistry {
259    &BUILTIN_COMPONENT_REGISTRY
260}
261
262pub fn render_builtin_component_listing() -> Vec<u8> {
263    BUILTIN_COMPONENT_REGISTRY.render_sysfs_listing()
264}