sal_virt/rfs/builder.rs
1use super::{
2 cmd::execute_rfs_command,
3 error::RfsError,
4 types::{Mount, MountType, StoreSpec},
5};
6use std::collections::HashMap;
7
8/// Builder for RFS mount operations
9#[derive(Clone)]
10pub struct RfsBuilder {
11 /// Source path or URL
12 source: String,
13 /// Target mount point
14 target: String,
15 /// Mount type
16 mount_type: MountType,
17 /// Mount options
18 options: HashMap<String, String>,
19 /// Mount ID
20 #[allow(dead_code)]
21 mount_id: Option<String>,
22 /// Debug mode
23 debug: bool,
24}
25
26impl RfsBuilder {
27 /// Create a new RFS builder
28 ///
29 /// # Arguments
30 ///
31 /// * `source` - Source path or URL
32 /// * `target` - Target mount point
33 /// * `mount_type` - Mount type
34 ///
35 /// # Returns
36 ///
37 /// * `Self` - New RFS builder
38 pub fn new(source: &str, target: &str, mount_type: MountType) -> Self {
39 Self {
40 source: source.to_string(),
41 target: target.to_string(),
42 mount_type,
43 options: HashMap::new(),
44 mount_id: None,
45 debug: false,
46 }
47 }
48
49 /// Add a mount option
50 ///
51 /// # Arguments
52 ///
53 /// * `key` - Option key
54 /// * `value` - Option value
55 ///
56 /// # Returns
57 ///
58 /// * `Self` - Updated RFS builder for method chaining
59 pub fn with_option(mut self, key: &str, value: &str) -> Self {
60 self.options.insert(key.to_string(), value.to_string());
61 self
62 }
63
64 /// Add multiple mount options
65 ///
66 /// # Arguments
67 ///
68 /// * `options` - Map of option keys to values
69 ///
70 /// # Returns
71 ///
72 /// * `Self` - Updated RFS builder for method chaining
73 pub fn with_options(mut self, options: HashMap<&str, &str>) -> Self {
74 for (key, value) in options {
75 self.options.insert(key.to_string(), value.to_string());
76 }
77 self
78 }
79
80 /// Set debug mode
81 ///
82 /// # Arguments
83 ///
84 /// * `debug` - Whether to enable debug output
85 ///
86 /// # Returns
87 ///
88 /// * `Self` - Updated RFS builder for method chaining
89 pub fn with_debug(mut self, debug: bool) -> Self {
90 self.debug = debug;
91 self
92 }
93
94 /// Get the source path
95 ///
96 /// # Returns
97 ///
98 /// * `&str` - Source path
99 pub fn source(&self) -> &str {
100 &self.source
101 }
102
103 /// Get the target path
104 ///
105 /// # Returns
106 ///
107 /// * `&str` - Target path
108 pub fn target(&self) -> &str {
109 &self.target
110 }
111
112 /// Get the mount type
113 ///
114 /// # Returns
115 ///
116 /// * `&MountType` - Mount type
117 pub fn mount_type(&self) -> &MountType {
118 &self.mount_type
119 }
120
121 /// Get the options
122 ///
123 /// # Returns
124 ///
125 /// * `&HashMap<String, String>` - Mount options
126 pub fn options(&self) -> &HashMap<String, String> {
127 &self.options
128 }
129
130 /// Get debug mode
131 ///
132 /// # Returns
133 ///
134 /// * `bool` - Whether debug mode is enabled
135 pub fn debug(&self) -> bool {
136 self.debug
137 }
138
139 /// Mount the filesystem
140 ///
141 /// # Returns
142 ///
143 /// * `Result<Mount, RfsError>` - Mount information or error
144 pub fn mount(self) -> Result<Mount, RfsError> {
145 // Build the command string
146 let mut cmd = String::from("mount -t ");
147 cmd.push_str(&self.mount_type.to_string());
148
149 // Add options if any
150 if !self.options.is_empty() {
151 cmd.push_str(" -o ");
152 let mut first = true;
153 for (key, value) in &self.options {
154 if !first {
155 cmd.push_str(",");
156 }
157 cmd.push_str(key);
158 cmd.push_str("=");
159 cmd.push_str(value);
160 first = false;
161 }
162 }
163
164 // Add source and target
165 cmd.push_str(" ");
166 cmd.push_str(&self.source);
167 cmd.push_str(" ");
168 cmd.push_str(&self.target);
169
170 // Split the command into arguments
171 let args: Vec<&str> = cmd.split_whitespace().collect();
172
173 // Execute the command
174 let result = execute_rfs_command(&args)?;
175
176 // Parse the output to get the mount ID
177 let mount_id = result.stdout.trim().to_string();
178 if mount_id.is_empty() {
179 return Err(RfsError::MountFailed("Failed to get mount ID".to_string()));
180 }
181
182 // Create and return the Mount struct
183 Ok(Mount {
184 id: mount_id,
185 source: self.source,
186 target: self.target,
187 fs_type: self.mount_type.to_string(),
188 options: self
189 .options
190 .iter()
191 .map(|(k, v)| format!("{}={}", k, v))
192 .collect(),
193 })
194 }
195
196 /// Unmount the filesystem
197 ///
198 /// # Returns
199 ///
200 /// * `Result<(), RfsError>` - Success or error
201 pub fn unmount(&self) -> Result<(), RfsError> {
202 // Execute the unmount command
203 let result = execute_rfs_command(&["unmount", &self.target])?;
204
205 // Check for errors
206 if !result.success {
207 return Err(RfsError::UnmountFailed(format!(
208 "Failed to unmount {}: {}",
209 self.target, result.stderr
210 )));
211 }
212
213 Ok(())
214 }
215}
216
217/// Builder for RFS pack operations
218#[derive(Clone)]
219pub struct PackBuilder {
220 /// Directory to pack
221 directory: String,
222 /// Output file
223 output: String,
224 /// Store specifications
225 store_specs: Vec<StoreSpec>,
226 /// Debug mode
227 debug: bool,
228}
229
230impl PackBuilder {
231 /// Create a new pack builder
232 ///
233 /// # Arguments
234 ///
235 /// * `directory` - Directory to pack
236 /// * `output` - Output file
237 ///
238 /// # Returns
239 ///
240 /// * `Self` - New pack builder
241 pub fn new(directory: &str, output: &str) -> Self {
242 Self {
243 directory: directory.to_string(),
244 output: output.to_string(),
245 store_specs: Vec::new(),
246 debug: false,
247 }
248 }
249
250 /// Add a store specification
251 ///
252 /// # Arguments
253 ///
254 /// * `store_spec` - Store specification
255 ///
256 /// # Returns
257 ///
258 /// * `Self` - Updated pack builder for method chaining
259 pub fn with_store_spec(mut self, store_spec: StoreSpec) -> Self {
260 self.store_specs.push(store_spec);
261 self
262 }
263
264 /// Add multiple store specifications
265 ///
266 /// # Arguments
267 ///
268 /// * `store_specs` - Store specifications
269 ///
270 /// # Returns
271 ///
272 /// * `Self` - Updated pack builder for method chaining
273 pub fn with_store_specs(mut self, store_specs: Vec<StoreSpec>) -> Self {
274 self.store_specs.extend(store_specs);
275 self
276 }
277
278 /// Set debug mode
279 ///
280 /// # Arguments
281 ///
282 /// * `debug` - Whether to enable debug output
283 ///
284 /// # Returns
285 ///
286 /// * `Self` - Updated pack builder for method chaining
287 pub fn with_debug(mut self, debug: bool) -> Self {
288 self.debug = debug;
289 self
290 }
291
292 /// Get the directory path
293 ///
294 /// # Returns
295 ///
296 /// * `&str` - Directory path
297 pub fn directory(&self) -> &str {
298 &self.directory
299 }
300
301 /// Get the output path
302 ///
303 /// # Returns
304 ///
305 /// * `&str` - Output path
306 pub fn output(&self) -> &str {
307 &self.output
308 }
309
310 /// Get the store specifications
311 ///
312 /// # Returns
313 ///
314 /// * `&Vec<StoreSpec>` - Store specifications
315 pub fn store_specs(&self) -> &Vec<StoreSpec> {
316 &self.store_specs
317 }
318
319 /// Get debug mode
320 ///
321 /// # Returns
322 ///
323 /// * `bool` - Whether debug mode is enabled
324 pub fn debug(&self) -> bool {
325 self.debug
326 }
327
328 /// Pack the directory
329 ///
330 /// # Returns
331 ///
332 /// * `Result<(), RfsError>` - Success or error
333 pub fn pack(self) -> Result<(), RfsError> {
334 // Build the command string
335 let mut cmd = String::from("pack -m ");
336 cmd.push_str(&self.output);
337
338 // Add store specs if any
339 if !self.store_specs.is_empty() {
340 cmd.push_str(" -s ");
341 let mut first = true;
342 for spec in &self.store_specs {
343 if !first {
344 cmd.push_str(",");
345 }
346 let spec_str = spec.to_string();
347 cmd.push_str(&spec_str);
348 first = false;
349 }
350 }
351
352 // Add directory
353 cmd.push_str(" ");
354 cmd.push_str(&self.directory);
355
356 // Split the command into arguments
357 let args: Vec<&str> = cmd.split_whitespace().collect();
358
359 // Execute the command
360 let result = execute_rfs_command(&args)?;
361
362 // Check for errors
363 if !result.success {
364 return Err(RfsError::PackFailed(format!(
365 "Failed to pack {}: {}",
366 self.directory, result.stderr
367 )));
368 }
369
370 Ok(())
371 }
372}