sal/run_builder_implementation_plan.md
2025-04-04 15:05:48 +02:00

4.5 KiB

Run Builder Implementation Plan

This document outlines the plan for refactoring the run.rs module to use the builder pattern.

Current Implementation Analysis

The current implementation has several functions for running commands and scripts:

  • run_command and run_command_silent for single commands
  • run_script and run_script_silent for multiline scripts
  • run and run_silent as convenience functions that detect whether the input is a command or script

These functions don't support all the options we want (die, async, log), and they don't follow the builder pattern.

Builder Pattern Implementation Plan

1. Create a RunBuilder struct

pub struct RunBuilder<'a> {
    cmd: &'a str,
    die: bool,
    silent: bool,
    async_exec: bool,
    log: bool,
}

2. Implement Default Values and Builder Methods

impl<'a> RunBuilder<'a> {
    pub fn new(cmd: &'a str) -> Self {
        Self {
            cmd,
            die: true,         // Default: true
            silent: false,     // Default: false
            async_exec: false, // Default: false
            log: false,        // Default: false
        }
    }

    pub fn die(mut self, die: bool) -> Self {
        self.die = die;
        self
    }

    pub fn silent(mut self, silent: bool) -> Self {
        self.silent = silent;
        self
    }

    pub fn async_exec(mut self, async_exec: bool) -> Self {
        self.async_exec = async_exec;
        self
    }

    pub fn log(mut self, log: bool) -> Self {
        self.log = log;
        self
    }

    pub fn execute(self) -> Result<CommandResult, RunError> {
        // Implementation will go here
    }
}

3. Implement the execute Method

The execute method will:

  1. Determine if the command is a script or a single command
  2. Handle the async_exec option by spawning a process without waiting
  3. Handle the log option by logging command execution if enabled
  4. Handle the die option by returning a CommandResult instead of an Err when die=false
  5. Use the existing internal functions for the actual execution

4. Create a Public Function to Start the Builder

pub fn run(cmd: &str) -> RunBuilder {
    RunBuilder::new(cmd)
}

5. Update Existing Functions for Backward Compatibility

Update the existing functions to use the new builder pattern internally for backward compatibility.

Structure Diagram

classDiagram
    class RunBuilder {
        +String cmd
        +bool die
        +bool silent
        +bool async_exec
        +bool log
        +new(cmd: &str) RunBuilder
        +die(bool) RunBuilder
        +silent(bool) RunBuilder
        +async_exec(bool) RunBuilder
        +log(bool) RunBuilder
        +execute() Result<CommandResult, RunError>
    }
    
    class CommandResult {
        +String stdout
        +String stderr
        +bool success
        +int code
    }
    
    RunBuilder ..> CommandResult : produces
    
    note for RunBuilder "Builder pattern implementation\nfor command execution"

Implementation Details

Handling the async_exec Option

When async_exec is true, we'll spawn the process but not wait for it to complete. We'll return a CommandResult with:

  • Empty stdout and stderr
  • success = true (since we don't know the outcome)
  • code = 0 (since we don't know the exit code)

Handling the log Option

When log is true, we'll log the command execution with a "[LOG]" prefix. For example:

[LOG] Executing command: ls -la

Handling the die Option

When die is false and a command fails, instead of returning an Err, we'll return a CommandResult with:

  • success = false
  • The appropriate error message in stderr
  • code = -1 or the actual exit code if available

Usage Examples

After implementation, users will be able to use the builder pattern like this:

// Simple usage with defaults
let result = run("ls -la").execute()?;

// With options
let result = run("ls -la")
    .silent(true)
    .die(false)
    .execute()?;

// Async execution
run("long_running_command")
    .async_exec(true)
    .execute()?;

// With logging
let result = run("important_command")
    .log(true)
    .execute()?;

// Script execution
let result = run("echo 'Hello'\necho 'World'")
    .silent(true)
    .execute()?;

Implementation Steps

  1. Add the RunBuilder struct and its methods
  2. Implement the execute method
  3. Create the public run function
  4. Update the existing functions to use the builder pattern internally
  5. Add tests for the new functionality
  6. Update documentation