...
This commit is contained in:
179
run_builder_implementation_plan.md
Normal file
179
run_builder_implementation_plan.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# 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
|
||||
|
||||
```rust
|
||||
pub struct RunBuilder<'a> {
|
||||
cmd: &'a str,
|
||||
die: bool,
|
||||
silent: bool,
|
||||
async_exec: bool,
|
||||
log: bool,
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Implement Default Values and Builder Methods
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```rust
|
||||
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
|
||||
|
||||
```mermaid
|
||||
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:
|
||||
|
||||
```rust
|
||||
// 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
|
||||
Reference in New Issue
Block a user