sal-virt 0.1.0

SAL Virt - Virtualization and containerization tools including Buildah, Nerdctl, and RFS
Documentation
use super::{
    cmd::execute_rfs_command,
    error::RfsError,
    types::{Mount, MountType, StoreSpec},
};
use std::collections::HashMap;

/// 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
    #[allow(dead_code)]
    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
    }

    /// Get the source path
    ///
    /// # Returns
    ///
    /// * `&str` - Source path
    pub fn source(&self) -> &str {
        &self.source
    }

    /// Get the target path
    ///
    /// # Returns
    ///
    /// * `&str` - Target path
    pub fn target(&self) -> &str {
        &self.target
    }

    /// Get the mount type
    ///
    /// # Returns
    ///
    /// * `&MountType` - Mount type
    pub fn mount_type(&self) -> &MountType {
        &self.mount_type
    }

    /// Get the options
    ///
    /// # Returns
    ///
    /// * `&HashMap<String, String>` - Mount options
    pub fn options(&self) -> &HashMap<String, String> {
        &self.options
    }

    /// Get debug mode
    ///
    /// # Returns
    ///
    /// * `bool` - Whether debug mode is enabled
    pub fn debug(&self) -> bool {
        self.debug
    }

    /// 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
    }

    /// Get the directory path
    ///
    /// # Returns
    ///
    /// * `&str` - Directory path
    pub fn directory(&self) -> &str {
        &self.directory
    }

    /// Get the output path
    ///
    /// # Returns
    ///
    /// * `&str` - Output path
    pub fn output(&self) -> &str {
        &self.output
    }

    /// Get the store specifications
    ///
    /// # Returns
    ///
    /// * `&Vec<StoreSpec>` - Store specifications
    pub fn store_specs(&self) -> &Vec<StoreSpec> {
        &self.store_specs
    }

    /// Get debug mode
    ///
    /// # Returns
    ///
    /// * `bool` - Whether debug mode is enabled
    pub fn debug(&self) -> bool {
        self.debug
    }

    /// 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(())
    }
}