agent_os_kernel/
mount_plugin.rs1use 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}