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
andrun_command_silent
for single commandsrun_script
andrun_script_silent
for multiline scriptsrun
andrun_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:
- Determine if the command is a script or a single command
- Handle the
async_exec
option by spawning a process without waiting - Handle the
log
option by logging command execution if enabled - Handle the
die
option by returning a CommandResult instead of an Err when die=false - 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
- Add the
RunBuilder
struct and its methods - Implement the
execute
method - Create the public
run
function - Update the existing functions to use the builder pattern internally
- Add tests for the new functionality
- Update documentation