Skip to main content

agent_os_kernel/
mount_plugin.rs

1use crate::mount_table::MountedFileSystem;
2use crate::vfs::VfsError;
3use serde_json::Value;
4use std::collections::BTreeMap;
5use std::error::Error;
6use std::fmt;
7
8#[derive(Debug)]
9pub struct OpenFileSystemPluginRequest<'a, Context> {
10    pub vm_id: &'a str,
11    pub guest_path: &'a str,
12    pub read_only: bool,
13    pub config: &'a Value,
14    pub context: &'a Context,
15}
16
17pub trait FileSystemPluginFactory<Context>: Send + Sync {
18    fn plugin_id(&self) -> &'static str;
19    fn open(
20        &self,
21        request: OpenFileSystemPluginRequest<'_, Context>,
22    ) -> Result<Box<dyn MountedFileSystem>, PluginError>;
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct PluginError {
27    code: &'static str,
28    message: String,
29}
30
31impl PluginError {
32    pub fn code(&self) -> &'static str {
33        self.code
34    }
35
36    pub fn message(&self) -> &str {
37        &self.message
38    }
39
40    pub fn new(code: &'static str, message: impl Into<String>) -> Self {
41        Self {
42            code,
43            message: message.into(),
44        }
45    }
46
47    pub fn unsupported(message: impl Into<String>) -> Self {
48        Self::new("ENOSYS", message)
49    }
50
51    pub fn invalid_input(message: impl Into<String>) -> Self {
52        Self::new("EINVAL", message)
53    }
54
55    pub fn already_exists(message: impl Into<String>) -> Self {
56        Self::new("EEXIST", message)
57    }
58}
59
60impl fmt::Display for PluginError {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        write!(f, "{}: {}", self.code, self.message)
63    }
64}
65
66impl Error for PluginError {}
67
68impl From<VfsError> for PluginError {
69    fn from(error: VfsError) -> Self {
70        Self::new(error.code(), error.message().to_owned())
71    }
72}
73
74pub struct FileSystemPluginRegistry<Context> {
75    factories: BTreeMap<String, Box<dyn FileSystemPluginFactory<Context>>>,
76}
77
78impl<Context> Default for FileSystemPluginRegistry<Context> {
79    fn default() -> Self {
80        Self::new()
81    }
82}
83
84impl<Context> FileSystemPluginRegistry<Context> {
85    pub fn new() -> Self {
86        Self {
87            factories: BTreeMap::new(),
88        }
89    }
90
91    pub fn register(
92        &mut self,
93        factory: impl FileSystemPluginFactory<Context> + 'static,
94    ) -> Result<(), PluginError> {
95        let plugin_id = factory.plugin_id();
96        if self.factories.contains_key(plugin_id) {
97            return Err(PluginError::already_exists(format!(
98                "filesystem plugin already registered: {plugin_id}"
99            )));
100        }
101
102        self.factories
103            .insert(plugin_id.to_owned(), Box::new(factory));
104        Ok(())
105    }
106
107    pub fn open(
108        &self,
109        plugin_id: &str,
110        request: OpenFileSystemPluginRequest<'_, Context>,
111    ) -> Result<Box<dyn MountedFileSystem>, PluginError> {
112        let Some(factory) = self.factories.get(plugin_id) else {
113            return Err(PluginError::unsupported(format!(
114                "filesystem plugin is not registered: {plugin_id}"
115            )));
116        };
117
118        factory.open(request)
119    }
120
121    pub fn plugin_ids(&self) -> Vec<String> {
122        self.factories.keys().cloned().collect()
123    }
124}