...
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
pub mod buildah;
|
||||
pub mod nerdctl;
|
||||
pub mod nerdctl;
|
||||
pub mod rfs;
|
280
src/virt/rfs/builder.rs
Normal file
280
src/virt/rfs/builder.rs
Normal file
@@ -0,0 +1,280 @@
|
||||
use std::collections::HashMap;
|
||||
use super::{
|
||||
error::RfsError,
|
||||
cmd::execute_rfs_command,
|
||||
types::{Mount, MountType, StoreSpec},
|
||||
};
|
||||
|
||||
/// Builder for RFS mount operations
|
||||
#[derive(Clone)]
|
||||
pub struct RfsBuilder {
|
||||
/// Source path or URL
|
||||
source: String,
|
||||
/// Target mount point
|
||||
target: String,
|
||||
/// Mount type
|
||||
mount_type: MountType,
|
||||
/// Mount options
|
||||
options: HashMap<String, String>,
|
||||
/// Mount ID
|
||||
mount_id: Option<String>,
|
||||
/// Debug mode
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl RfsBuilder {
|
||||
/// Create a new RFS builder
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `source` - Source path or URL
|
||||
/// * `target` - Target mount point
|
||||
/// * `mount_type` - Mount type
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - New RFS builder
|
||||
pub fn new(source: &str, target: &str, mount_type: MountType) -> Self {
|
||||
Self {
|
||||
source: source.to_string(),
|
||||
target: target.to_string(),
|
||||
mount_type,
|
||||
options: HashMap::new(),
|
||||
mount_id: None,
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a mount option
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key` - Option key
|
||||
/// * `value` - Option value
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated RFS builder for method chaining
|
||||
pub fn with_option(mut self, key: &str, value: &str) -> Self {
|
||||
self.options.insert(key.to_string(), value.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple mount options
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `options` - Map of option keys to values
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated RFS builder for method chaining
|
||||
pub fn with_options(mut self, options: HashMap<&str, &str>) -> Self {
|
||||
for (key, value) in options {
|
||||
self.options.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set debug mode
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `debug` - Whether to enable debug output
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated RFS builder for method chaining
|
||||
pub fn with_debug(mut self, debug: bool) -> Self {
|
||||
self.debug = debug;
|
||||
self
|
||||
}
|
||||
|
||||
/// Mount the filesystem
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Mount, RfsError>` - Mount information or error
|
||||
pub fn mount(self) -> Result<Mount, RfsError> {
|
||||
// Build the command string
|
||||
let mut cmd = String::from("mount -t ");
|
||||
cmd.push_str(&self.mount_type.to_string());
|
||||
|
||||
// Add options if any
|
||||
if !self.options.is_empty() {
|
||||
cmd.push_str(" -o ");
|
||||
let mut first = true;
|
||||
for (key, value) in &self.options {
|
||||
if !first {
|
||||
cmd.push_str(",");
|
||||
}
|
||||
cmd.push_str(key);
|
||||
cmd.push_str("=");
|
||||
cmd.push_str(value);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add source and target
|
||||
cmd.push_str(" ");
|
||||
cmd.push_str(&self.source);
|
||||
cmd.push_str(" ");
|
||||
cmd.push_str(&self.target);
|
||||
|
||||
// Split the command into arguments
|
||||
let args: Vec<&str> = cmd.split_whitespace().collect();
|
||||
|
||||
// Execute the command
|
||||
let result = execute_rfs_command(&args)?;
|
||||
|
||||
// Parse the output to get the mount ID
|
||||
let mount_id = result.stdout.trim().to_string();
|
||||
if mount_id.is_empty() {
|
||||
return Err(RfsError::MountFailed("Failed to get mount ID".to_string()));
|
||||
}
|
||||
|
||||
// Create and return the Mount struct
|
||||
Ok(Mount {
|
||||
id: mount_id,
|
||||
source: self.source,
|
||||
target: self.target,
|
||||
fs_type: self.mount_type.to_string(),
|
||||
options: self.options.iter().map(|(k, v)| format!("{}={}", k, v)).collect(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Unmount the filesystem
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn unmount(&self) -> Result<(), RfsError> {
|
||||
// Execute the unmount command
|
||||
let result = execute_rfs_command(&["unmount", &self.target])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::UnmountFailed(format!("Failed to unmount {}: {}", self.target, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for RFS pack operations
|
||||
#[derive(Clone)]
|
||||
pub struct PackBuilder {
|
||||
/// Directory to pack
|
||||
directory: String,
|
||||
/// Output file
|
||||
output: String,
|
||||
/// Store specifications
|
||||
store_specs: Vec<StoreSpec>,
|
||||
/// Debug mode
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl PackBuilder {
|
||||
/// Create a new pack builder
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `directory` - Directory to pack
|
||||
/// * `output` - Output file
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - New pack builder
|
||||
pub fn new(directory: &str, output: &str) -> Self {
|
||||
Self {
|
||||
directory: directory.to_string(),
|
||||
output: output.to_string(),
|
||||
store_specs: Vec::new(),
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a store specification
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `store_spec` - Store specification
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated pack builder for method chaining
|
||||
pub fn with_store_spec(mut self, store_spec: StoreSpec) -> Self {
|
||||
self.store_specs.push(store_spec);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple store specifications
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `store_specs` - Store specifications
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated pack builder for method chaining
|
||||
pub fn with_store_specs(mut self, store_specs: Vec<StoreSpec>) -> Self {
|
||||
self.store_specs.extend(store_specs);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set debug mode
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `debug` - Whether to enable debug output
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated pack builder for method chaining
|
||||
pub fn with_debug(mut self, debug: bool) -> Self {
|
||||
self.debug = debug;
|
||||
self
|
||||
}
|
||||
|
||||
/// Pack the directory
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn pack(self) -> Result<(), RfsError> {
|
||||
// Build the command string
|
||||
let mut cmd = String::from("pack -m ");
|
||||
cmd.push_str(&self.output);
|
||||
|
||||
// Add store specs if any
|
||||
if !self.store_specs.is_empty() {
|
||||
cmd.push_str(" -s ");
|
||||
let mut first = true;
|
||||
for spec in &self.store_specs {
|
||||
if !first {
|
||||
cmd.push_str(",");
|
||||
}
|
||||
let spec_str = spec.to_string();
|
||||
cmd.push_str(&spec_str);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Add directory
|
||||
cmd.push_str(" ");
|
||||
cmd.push_str(&self.directory);
|
||||
|
||||
// Split the command into arguments
|
||||
let args: Vec<&str> = cmd.split_whitespace().collect();
|
||||
|
||||
// Execute the command
|
||||
let result = execute_rfs_command(&args)?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::PackFailed(format!("Failed to pack {}: {}", self.directory, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
62
src/virt/rfs/cmd.rs
Normal file
62
src/virt/rfs/cmd.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use crate::process::{run_command, CommandResult};
|
||||
use super::error::RfsError;
|
||||
use std::thread_local;
|
||||
use std::cell::RefCell;
|
||||
|
||||
// Thread-local storage for debug flag
|
||||
thread_local! {
|
||||
static DEBUG: RefCell<bool> = RefCell::new(false);
|
||||
}
|
||||
|
||||
/// Set the thread-local debug flag
|
||||
pub fn set_thread_local_debug(debug: bool) {
|
||||
DEBUG.with(|d| {
|
||||
*d.borrow_mut() = debug;
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the current thread-local debug flag
|
||||
pub fn thread_local_debug() -> bool {
|
||||
DEBUG.with(|d| {
|
||||
*d.borrow()
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute an RFS command with the given arguments
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `args` - Command arguments
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<CommandResult, RfsError>` - Command result or error
|
||||
pub fn execute_rfs_command(args: &[&str]) -> Result<CommandResult, RfsError> {
|
||||
let debug = thread_local_debug();
|
||||
|
||||
// Construct the command string
|
||||
let mut cmd = String::from("rfs");
|
||||
for arg in args {
|
||||
cmd.push(' ');
|
||||
cmd.push_str(arg);
|
||||
}
|
||||
|
||||
if debug {
|
||||
println!("Executing RFS command: {}", cmd);
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
let result = run_command(&cmd)
|
||||
.map_err(|e| RfsError::CommandFailed(format!("Failed to execute RFS command: {}", e)))?;
|
||||
|
||||
if debug {
|
||||
println!("RFS command result: {:?}", result);
|
||||
}
|
||||
|
||||
// Check if the command was successful
|
||||
if !result.success && !result.stderr.is_empty() {
|
||||
return Err(RfsError::CommandFailed(result.stderr));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
43
src/virt/rfs/error.rs
Normal file
43
src/virt/rfs/error.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
|
||||
/// Error types for RFS operations
|
||||
#[derive(Debug)]
|
||||
pub enum RfsError {
|
||||
/// Command execution failed
|
||||
CommandFailed(String),
|
||||
/// Invalid argument provided
|
||||
InvalidArgument(String),
|
||||
/// Mount operation failed
|
||||
MountFailed(String),
|
||||
/// Unmount operation failed
|
||||
UnmountFailed(String),
|
||||
/// List operation failed
|
||||
ListFailed(String),
|
||||
/// Pack operation failed
|
||||
PackFailed(String),
|
||||
/// Other error
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for RfsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
RfsError::CommandFailed(msg) => write!(f, "RFS command failed: {}", msg),
|
||||
RfsError::InvalidArgument(msg) => write!(f, "Invalid argument: {}", msg),
|
||||
RfsError::MountFailed(msg) => write!(f, "Mount failed: {}", msg),
|
||||
RfsError::UnmountFailed(msg) => write!(f, "Unmount failed: {}", msg),
|
||||
RfsError::ListFailed(msg) => write!(f, "List failed: {}", msg),
|
||||
RfsError::PackFailed(msg) => write!(f, "Pack failed: {}", msg),
|
||||
RfsError::Other(msg) => write!(f, "Other error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for RfsError {}
|
||||
|
||||
impl From<std::io::Error> for RfsError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
RfsError::Other(format!("IO error: {}", error))
|
||||
}
|
||||
}
|
15
src/virt/rfs/mod.rs
Normal file
15
src/virt/rfs/mod.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
mod cmd;
|
||||
mod error;
|
||||
mod mount;
|
||||
mod pack;
|
||||
mod builder;
|
||||
mod types;
|
||||
|
||||
pub use error::RfsError;
|
||||
pub use builder::{RfsBuilder, PackBuilder};
|
||||
pub use types::{Mount, MountType, StoreSpec};
|
||||
pub use mount::{list_mounts, unmount_all, unmount, get_mount_info};
|
||||
pub use pack::{pack_directory, unpack, list_contents, verify};
|
||||
|
||||
// Re-export the execute_rfs_command function for use in other modules
|
||||
pub(crate) use cmd::execute_rfs_command;
|
142
src/virt/rfs/mount.rs
Normal file
142
src/virt/rfs/mount.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use super::{
|
||||
error::RfsError,
|
||||
cmd::execute_rfs_command,
|
||||
types::Mount,
|
||||
};
|
||||
|
||||
/// List all mounted filesystems
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Vec<Mount>, RfsError>` - List of mounts or error
|
||||
pub fn list_mounts() -> Result<Vec<Mount>, RfsError> {
|
||||
// Execute the list command
|
||||
let result = execute_rfs_command(&["list", "--json"])?;
|
||||
|
||||
// Parse the JSON output
|
||||
match serde_json::from_str::<serde_json::Value>(&result.stdout) {
|
||||
Ok(json) => {
|
||||
if let serde_json::Value::Array(mounts_json) = json {
|
||||
let mut mounts = Vec::new();
|
||||
|
||||
for mount_json in mounts_json {
|
||||
// Extract mount ID
|
||||
let id = match mount_json.get("id").and_then(|v| v.as_str()) {
|
||||
Some(id) => id.to_string(),
|
||||
None => return Err(RfsError::ListFailed("Missing mount ID".to_string())),
|
||||
};
|
||||
|
||||
// Extract source
|
||||
let source = match mount_json.get("source").and_then(|v| v.as_str()) {
|
||||
Some(source) => source.to_string(),
|
||||
None => return Err(RfsError::ListFailed("Missing source".to_string())),
|
||||
};
|
||||
|
||||
// Extract target
|
||||
let target = match mount_json.get("target").and_then(|v| v.as_str()) {
|
||||
Some(target) => target.to_string(),
|
||||
None => return Err(RfsError::ListFailed("Missing target".to_string())),
|
||||
};
|
||||
|
||||
// Extract filesystem type
|
||||
let fs_type = match mount_json.get("type").and_then(|v| v.as_str()) {
|
||||
Some(fs_type) => fs_type.to_string(),
|
||||
None => return Err(RfsError::ListFailed("Missing filesystem type".to_string())),
|
||||
};
|
||||
|
||||
// Extract options
|
||||
let options = match mount_json.get("options").and_then(|v| v.as_array()) {
|
||||
Some(options_array) => {
|
||||
let mut options_vec = Vec::new();
|
||||
for option_value in options_array {
|
||||
if let Some(option_str) = option_value.as_str() {
|
||||
options_vec.push(option_str.to_string());
|
||||
}
|
||||
}
|
||||
options_vec
|
||||
},
|
||||
None => Vec::new(), // Empty vector if no options found
|
||||
};
|
||||
|
||||
// Create Mount struct and add to vector
|
||||
mounts.push(Mount {
|
||||
id,
|
||||
source,
|
||||
target,
|
||||
fs_type,
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(mounts)
|
||||
} else {
|
||||
Err(RfsError::ListFailed("Expected JSON array".to_string()))
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
Err(RfsError::ListFailed(format!("Failed to parse mount list JSON: {}", e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unmount a filesystem by target path
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `target` - Target mount point
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn unmount(target: &str) -> Result<(), RfsError> {
|
||||
// Execute the unmount command
|
||||
let result = execute_rfs_command(&["unmount", target])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::UnmountFailed(format!("Failed to unmount {}: {}", target, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unmount all filesystems
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn unmount_all() -> Result<(), RfsError> {
|
||||
// Execute the unmount all command
|
||||
let result = execute_rfs_command(&["unmount", "--all"])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::UnmountFailed(format!("Failed to unmount all filesystems: {}", result.stderr)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get information about a mounted filesystem
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `target` - Target mount point
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<Mount, RfsError>` - Mount information or error
|
||||
pub fn get_mount_info(target: &str) -> Result<Mount, RfsError> {
|
||||
// Get all mounts
|
||||
let mounts = list_mounts()?;
|
||||
|
||||
// Find the mount with the specified target
|
||||
for mount in mounts {
|
||||
if mount.target == target {
|
||||
return Ok(mount);
|
||||
}
|
||||
}
|
||||
|
||||
// Mount not found
|
||||
Err(RfsError::Other(format!("No mount found at {}", target)))
|
||||
}
|
100
src/virt/rfs/pack.rs
Normal file
100
src/virt/rfs/pack.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use super::{
|
||||
error::RfsError,
|
||||
cmd::execute_rfs_command,
|
||||
types::StoreSpec,
|
||||
builder::PackBuilder,
|
||||
};
|
||||
|
||||
/// Pack a directory into a filesystem layer
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `directory` - Directory to pack
|
||||
/// * `output` - Output file
|
||||
/// * `store_specs` - Store specifications
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn pack_directory(directory: &str, output: &str, store_specs: &[StoreSpec]) -> Result<(), RfsError> {
|
||||
// Create a new pack builder
|
||||
let mut builder = PackBuilder::new(directory, output);
|
||||
|
||||
// Add store specs
|
||||
for spec in store_specs {
|
||||
builder = builder.with_store_spec(spec.clone());
|
||||
}
|
||||
|
||||
// Pack the directory
|
||||
builder.pack()
|
||||
}
|
||||
|
||||
/// Unpack a filesystem layer
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `input` - Input file
|
||||
/// * `directory` - Directory to unpack to
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<(), RfsError>` - Success or error
|
||||
pub fn unpack(input: &str, directory: &str) -> Result<(), RfsError> {
|
||||
// Execute the unpack command
|
||||
let result = execute_rfs_command(&["unpack", "-m", input, directory])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::Other(format!("Failed to unpack {}: {}", input, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List the contents of a filesystem layer
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `input` - Input file
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<String, RfsError>` - File listing or error
|
||||
pub fn list_contents(input: &str) -> Result<String, RfsError> {
|
||||
// Execute the list command
|
||||
let result = execute_rfs_command(&["list", "-m", input])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
return Err(RfsError::Other(format!("Failed to list contents of {}: {}", input, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(result.stdout)
|
||||
}
|
||||
|
||||
/// Verify a filesystem layer
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `input` - Input file
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Result<bool, RfsError>` - Whether the layer is valid or error
|
||||
pub fn verify(input: &str) -> Result<bool, RfsError> {
|
||||
// Execute the verify command
|
||||
let result = execute_rfs_command(&["verify", "-m", input])?;
|
||||
|
||||
// Check for errors
|
||||
if !result.success {
|
||||
// If the command failed but returned a specific error about verification,
|
||||
// return false instead of an error
|
||||
if result.stderr.contains("verification failed") {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
return Err(RfsError::Other(format!("Failed to verify {}: {}", input, result.stderr)));
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
117
src/virt/rfs/types.rs
Normal file
117
src/virt/rfs/types.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Represents a mounted filesystem
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Mount {
|
||||
/// Mount ID
|
||||
pub id: String,
|
||||
/// Source path or URL
|
||||
pub source: String,
|
||||
/// Target mount point
|
||||
pub target: String,
|
||||
/// Filesystem type
|
||||
pub fs_type: String,
|
||||
/// Mount options
|
||||
pub options: Vec<String>,
|
||||
}
|
||||
|
||||
/// Types of mounts supported by RFS
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MountType {
|
||||
/// Local filesystem
|
||||
Local,
|
||||
/// SSH remote filesystem
|
||||
SSH,
|
||||
/// S3 object storage
|
||||
S3,
|
||||
/// WebDAV remote filesystem
|
||||
WebDAV,
|
||||
/// Custom mount type
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
impl MountType {
|
||||
/// Convert mount type to string representation
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
MountType::Local => "local".to_string(),
|
||||
MountType::SSH => "ssh".to_string(),
|
||||
MountType::S3 => "s3".to_string(),
|
||||
MountType::WebDAV => "webdav".to_string(),
|
||||
MountType::Custom(s) => s.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a MountType from a string
|
||||
pub fn from_string(s: &str) -> Self {
|
||||
match s.to_lowercase().as_str() {
|
||||
"local" => MountType::Local,
|
||||
"ssh" => MountType::SSH,
|
||||
"s3" => MountType::S3,
|
||||
"webdav" => MountType::WebDAV,
|
||||
_ => MountType::Custom(s.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Store specification for packing operations
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StoreSpec {
|
||||
/// Store type (e.g., "file", "s3")
|
||||
pub spec_type: String,
|
||||
/// Store options
|
||||
pub options: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl StoreSpec {
|
||||
/// Create a new store specification
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `spec_type` - Store type (e.g., "file", "s3")
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - New store specification
|
||||
pub fn new(spec_type: &str) -> Self {
|
||||
Self {
|
||||
spec_type: spec_type.to_string(),
|
||||
options: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an option to the store specification
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `key` - Option key
|
||||
/// * `value` - Option value
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Self` - Updated store specification for method chaining
|
||||
pub fn with_option(mut self, key: &str, value: &str) -> Self {
|
||||
self.options.insert(key.to_string(), value.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Convert the store specification to a string
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `String` - String representation of the store specification
|
||||
pub fn to_string(&self) -> String {
|
||||
let mut result = self.spec_type.clone();
|
||||
|
||||
if !self.options.is_empty() {
|
||||
result.push_str(":");
|
||||
let options: Vec<String> = self.options
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}={}", k, v))
|
||||
.collect();
|
||||
result.push_str(&options.join(","));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user