179 lines
4.5 KiB
Markdown
179 lines
4.5 KiB
Markdown
# 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 |