sal/rfs_implementation_plan.md
2025-04-05 19:00:59 +02:00

11 KiB

RFS Wrapper Implementation Plan

Overview

We'll create a Rust wrapper for the RFS (Remote File System) tool that follows the builder pattern, similar to the existing implementations for buildah and nerdctl in the codebase. This wrapper will provide a fluent API for mounting, unmounting, listing mounts, configuring mount options, and packing directories into filesystem layers.

Module Structure

src/virt/rfs/
├── mod.rs           # Module exports and common types
├── cmd.rs           # Command execution functions
├── mount.rs         # Mount operations
├── pack.rs          # Packing operations
├── builder.rs       # Builder pattern implementation
├── types.rs         # Type definitions
└── error.rs         # Error handling

Implementation Details

1. Error Handling

// error.rs
#[derive(Debug)]
pub enum RfsError {
    CommandFailed(String),
    InvalidArgument(String),
    MountFailed(String),
    UnmountFailed(String),
    ListFailed(String),
    PackFailed(String),
    Other(String),
}

impl std::fmt::Display for RfsError {
    // Implementation
}

impl std::error::Error for RfsError {
    // Implementation
}

2. Command Execution

// cmd.rs
use crate::process::{run_command, CommandResult};
use super::error::RfsError;

pub fn execute_rfs_command(args: &[&str]) -> Result<CommandResult, RfsError> {
    // Implementation similar to buildah and nerdctl
}

3. Types

// types.rs
#[derive(Debug, Clone)]
pub struct Mount {
    pub id: String,
    pub source: String,
    pub target: String,
    pub fs_type: String,
    pub options: Vec<String>,
}

#[derive(Debug, Clone)]
pub enum MountType {
    Local,
    SSH,
    S3,
    WebDAV,
    // Other mount types
}

#[derive(Debug, Clone)]
pub struct StoreSpec {
    pub spec_type: String,
    pub options: std::collections::HashMap<String, String>,
}

impl StoreSpec {
    pub fn new(spec_type: &str) -> Self {
        Self {
            spec_type: spec_type.to_string(),
            options: std::collections::HashMap::new(),
        }
    }

    pub fn with_option(mut self, key: &str, value: &str) -> Self {
        self.options.insert(key.to_string(), value.to_string());
        self
    }

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

4. Builder Pattern

// builder.rs
use std::collections::HashMap;
use super::{Mount, MountType, RfsError, execute_rfs_command, StoreSpec};

#[derive(Clone)]
pub struct RfsBuilder {
    source: String,
    target: String,
    mount_type: MountType,
    options: HashMap<String, String>,
    mount_id: Option<String>,
    debug: bool,
}

impl RfsBuilder {
    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,
        }
    }
    
    pub fn with_option(mut self, key: &str, value: &str) -> Self {
        self.options.insert(key.to_string(), value.to_string());
        self
    }
    
    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
    }
    
    pub fn with_debug(mut self, debug: bool) -> Self {
        self.debug = debug;
        self
    }
    
    pub fn mount(self) -> Result<Mount, RfsError> {
        // Implementation
    }
    
    pub fn unmount(&self) -> Result<(), RfsError> {
        // Implementation
    }
    
    // Other methods
}

// Packing functionality
pub struct PackBuilder {
    directory: String,
    output: String,
    store_specs: Vec<StoreSpec>,
    debug: bool,
}

impl PackBuilder {
    pub fn new(directory: &str, output: &str) -> Self {
        Self {
            directory: directory.to_string(),
            output: output.to_string(),
            store_specs: Vec::new(),
            debug: false,
        }
    }
    
    pub fn with_store_spec(mut self, store_spec: StoreSpec) -> Self {
        self.store_specs.push(store_spec);
        self
    }
    
    pub fn with_store_specs(mut self, store_specs: Vec<StoreSpec>) -> Self {
        self.store_specs.extend(store_specs);
        self
    }
    
    pub fn with_debug(mut self, debug: bool) -> Self {
        self.debug = debug;
        self
    }
    
    pub fn pack(self) -> Result<(), RfsError> {
        // Implementation for packing a directory into a filesystem layer
        let mut args = vec!["pack"];
        
        // Add output file
        args.push("-m");
        args.push(&self.output);
        
        // Add store specs
        if !self.store_specs.is_empty() {
            args.push("-s");
            let specs: Vec<String> = self.store_specs
                .iter()
                .map(|spec| spec.to_string())
                .collect();
            args.push(&specs.join(","));
        }
        
        // Add directory
        args.push(&self.directory);
        
        // Convert to string slices for the command
        let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
        
        // Execute the command
        let result = execute_rfs_command(&args_str)?;
        
        // Check for errors
        if !result.success {
            return Err(RfsError::PackFailed(result.stderr));
        }
        
        Ok(())
    }
}

5. Mount Operations

// mount.rs
use super::{RfsBuilder, Mount, RfsError, execute_rfs_command};

pub fn list_mounts() -> Result<Vec<Mount>, RfsError> {
    // Implementation
}

pub fn unmount_all() -> Result<(), RfsError> {
    // Implementation
}

// Other mount-related functions

6. Pack Operations

// pack.rs
use super::{PackBuilder, StoreSpec, RfsError, execute_rfs_command};

pub fn pack_directory(directory: &str, output: &str, store_specs: &[StoreSpec]) -> Result<(), RfsError> {
    PackBuilder::new(directory, output)
        .with_store_specs(store_specs.to_vec())
        .pack()
}

// Other pack-related functions

7. Module Exports

// mod.rs
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};
pub use pack::pack_directory;

// Re-export the execute_rfs_command function for use in other modules
pub(crate) use cmd::execute_rfs_command;

Usage Examples

Mounting Example

use crate::virt::rfs::{RfsBuilder, MountType};

// Create a new RFS mount with builder pattern
let mount = RfsBuilder::new("user@example.com:/remote/path", "/local/mount/point", MountType::SSH)
    .with_option("port", "2222")
    .with_option("identity_file", "/path/to/key")
    .with_debug(true)
    .mount()?;

// List all mounts
let mounts = list_mounts()?;
for mount in mounts {
    println!("Mount ID: {}, Source: {}, Target: {}", mount.id, mount.source, mount.target);
}

// Unmount
mount.unmount()?;

Packing Example

use crate::virt::rfs::{PackBuilder, StoreSpec};

// Create store specifications
let store_spec1 = StoreSpec::new("file")
    .with_option("path", "/path/to/store");

let store_spec2 = StoreSpec::new("s3")
    .with_option("bucket", "my-bucket")
    .with_option("region", "us-east-1");

// Pack a directory with builder pattern
let result = PackBuilder::new("/path/to/directory", "output.fl")
    .with_store_spec(store_spec1)
    .with_store_spec(store_spec2)
    .with_debug(true)
    .pack()?;

// Or use the convenience function
pack_directory("/path/to/directory", "output.fl", &[store_spec1, store_spec2])?;

Rhai Integration

We'll also need to create a Rhai module to expose the RFS functionality to Rhai scripts:

// src/rhai/rfs.rs
use rhai::{Engine, EvalAltResult, RegisterFn};
use crate::virt::rfs::{RfsBuilder, MountType, list_mounts, unmount_all, PackBuilder, StoreSpec};

pub fn register(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // Register RFS functions
    engine.register_fn("rfs_mount", rfs_mount);
    engine.register_fn("rfs_unmount", rfs_unmount);
    engine.register_fn("rfs_list_mounts", rfs_list_mounts);
    engine.register_fn("rfs_unmount_all", rfs_unmount_all);
    engine.register_fn("rfs_pack", rfs_pack);
    
    Ok(())
}

// Function implementations
fn rfs_mount(source: &str, target: &str, mount_type: &str, options_map: rhai::Map) -> Result<(), Box<EvalAltResult>> {
    // Implementation
}

fn rfs_unmount(target: &str) -> Result<(), Box<EvalAltResult>> {
    // Implementation
}

fn rfs_list_mounts() -> Result<rhai::Array, Box<EvalAltResult>> {
    // Implementation
}

fn rfs_unmount_all() -> Result<(), Box<EvalAltResult>> {
    // Implementation
}

fn rfs_pack(directory: &str, output: &str, store_specs: &str) -> Result<(), Box<EvalAltResult>> {
    // Implementation
}

Implementation Flow

Here's a diagram showing the flow of the implementation:

classDiagram
    class RfsBuilder {
        +String source
        +String target
        +MountType mount_type
        +HashMap options
        +Option~String~ mount_id
        +bool debug
        +new(source, target, mount_type)
        +with_option(key, value)
        +with_options(options)
        +with_debug(debug)
        +mount()
        +unmount()
    }
    
    class PackBuilder {
        +String directory
        +String output
        +Vec~StoreSpec~ store_specs
        +bool debug
        +new(directory, output)
        +with_store_spec(store_spec)
        +with_store_specs(store_specs)
        +with_debug(debug)
        +pack()
    }
    
    class Mount {
        +String id
        +String source
        +String target
        +String fs_type
        +Vec~String~ options
    }
    
    class MountType {
        <<enumeration>>
        Local
        SSH
        S3
        WebDAV
    }
    
    class StoreSpec {
        +String spec_type
        +HashMap options
        +new(spec_type)
        +with_option(key, value)
        +to_string()
    }
    
    class RfsError {
        <<enumeration>>
        CommandFailed
        InvalidArgument
        MountFailed
        UnmountFailed
        ListFailed
        PackFailed
        Other
    }
    
    RfsBuilder --> Mount : creates
    RfsBuilder --> RfsError : may throw
    RfsBuilder --> MountType : uses
    PackBuilder --> RfsError : may throw
    PackBuilder --> StoreSpec : uses
    Mount --> RfsError : may throw

Implementation Steps

  1. Create the directory structure for the RFS module
  2. Implement the error handling module
  3. Implement the command execution module
  4. Define the types for mounts, mount operations, and store specifications
  5. Implement the builder pattern for RFS operations (mount and pack)
  6. Implement the mount operations
  7. Implement the pack operations
  8. Create the module exports
  9. Add Rhai integration
  10. Write tests for the implementation
  11. Update documentation