openrunner-rs 1.0.0

A Rust library for running OpenScript
Documentation
# OpenRunner-RS

[![Crates.io](https://img.shields.io/crates/v/openrunner-rs.svg)](https://crates.io/crates/openrunner-rs)
[![Documentation](https://docs.rs/openrunner-rs/badge.svg)](https://docs.rs/openrunner-rs)
[![CI](https://github.com/openrunner-dev/openrunner-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/openrunner-dev/openrunner-rs/actions)
[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](LICENSE-MIT)

**OpenRunner-RS** is a powerful, async-first Rust library for executing OpenScript code and shell scripts with fine-grained control over execution environment, I/O redirection, process lifecycle, and error handling.

## ๐Ÿš€ Features

- **Async-First Design**: Built on tokio for high-performance concurrent execution
- **Flexible Script Sources**: Execute from strings, files, or remote sources
- **Fine-Grained Control**: Full control over environment variables, working directory, I/O redirection
- **Process Management**: Spawn long-running processes with full lifecycle control
- **Timeout Support**: Built-in timeout handling with graceful process termination
- **Error Handling**: Comprehensive error types with detailed context
- **Convenience Macros**: Ergonomic macros for common use cases
- **Zero-Copy I/O**: Efficient handling of large script outputs
- **Cross-Platform**: Works on Linux, macOS, and Windows

## ๐Ÿ“ฆ Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
openrunner-rs = "1.0"
tokio = { version = "1", features = ["full"] }
```

## ๐Ÿƒ Quick Start

### Basic Script Execution

```rust
use openrunner_rs::{run, ScriptOptions};

#[tokio::main]
async fn main() -> openrunner_rs::Result<()> {
    // Simple script execution
    let options = ScriptOptions::new().openscript_path("/bin/sh");
    let result = run("echo 'Hello, OpenRunner!'", options).await?;
    
    println!("Exit code: {}", result.exit_code);
    println!("Output: {}", result.stdout);
    println!("Duration: {:?}", result.duration);
    
    Ok(())
}
```

### Advanced Configuration

```rust
use openrunner_rs::{run, ScriptOptions};
use std::time::Duration;

#[tokio::main]
async fn main() -> openrunner_rs::Result<()> {
    let options = ScriptOptions::new()
        .openscript_path("/bin/bash")
        .working_directory("/tmp")
        .env("LOG_LEVEL", "debug")
        .env("API_KEY", "secret")
        .timeout(Duration::from_secs(30))
        .args(vec!["arg1".to_string(), "arg2".to_string()]);
    
    let result = run(r#"
        echo "Working in: $(pwd)"
        echo "Log level: $LOG_LEVEL"
        echo "Arguments: $1 $2"
        sleep 2
        echo "Task completed!"
    "#, options).await?;
    
    if result.timed_out {
        println!("Script timed out!");
    } else {
        println!("Script completed successfully: {}", result.stdout);
    }
    
    Ok(())
}
```

### Process Spawning

```rust
use openrunner_rs::{spawn, ScriptOptions};

#[tokio::main]
async fn main() -> openrunner_rs::Result<()> {
    let options = ScriptOptions::new().openscript_path("/bin/sh");
    
    // Spawn a long-running background process
    let spawn_result = spawn(r#"
        for i in {1..10}; do
            echo "Processing item $i"
            sleep 1
        done
    "#, options).await?;
    
    println!("Spawned process with PID: {:?}", spawn_result.child.id());
    
    // Do other work while process runs...
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
    
    // Wait for completion
    let output = spawn_result.child.wait_with_output().await?;
    println!("Process completed: {}", String::from_utf8_lossy(&output.stdout));
    
    Ok(())
}
```

## ๐Ÿ› ๏ธ API Reference

### Core Functions

| Function | Description |
|----------|-------------|
| `run(script, options)` | Execute script and wait for completion |
| `run_file(path, options)` | Execute script from file |
| `spawn(script, options)` | Spawn script process without waiting |
| `spawn_file(path, options)` | Spawn script from file |

### Convenience Macros

| Macro | Description |
|-------|-------------|
| `run_script!(script, options?)` | Simplified script execution |
| `spawn_script!(script, options?)` | Simplified script spawning |
| `run_file_script!(path, options?)` | Simplified file execution |

### Configuration Options

```rust
ScriptOptions::new()
    .openscript_path("/path/to/interpreter")  // Script interpreter
    .working_directory("/working/dir")        // Working directory
    .env("KEY", "value")                      // Environment variables
    .args(vec!["arg1", "arg2"])              // Command arguments
    .timeout(Duration::from_secs(30))         // Execution timeout
    .stdin(IoOptions::Pipe)                   // Stdin handling
    .stdout(IoOptions::Pipe)                  // Stdout handling
    .stderr(IoOptions::Pipe)                  // Stderr handling
    .clear_env(true)                          // Clear environment
    .exit_on_error(false)                     // Continue on errors
    .print_commands(true)                     // Debug output
```

## ๐Ÿ“‹ Examples

### File Execution

```rust
use openrunner_rs::{run_file, ScriptOptions};
use std::path::PathBuf;

#[tokio::main]
async fn main() -> openrunner_rs::Result<()> {
    let script_path = PathBuf::from("./scripts/deploy.sh");
    let options = ScriptOptions::new()
        .openscript_path("/bin/bash")
        .env("ENVIRONMENT", "production");
    
    let result = run_file(&script_path, options).await?;
    println!("Deployment result: {}", result.stdout);
    Ok(())
}
```

### Error Handling

```rust
use openrunner_rs::{run, ScriptOptions, Error};

#[tokio::main]
async fn main() {
    let options = ScriptOptions::new().openscript_path("/bin/sh");
    
    match run("exit 1", options).await {
        Ok(result) => {
            if result.exit_code != 0 {
                println!("Script failed with code: {}", result.exit_code);
                println!("Error output: {}", result.stderr);
            }
        }
        Err(Error::Timeout(duration)) => {
            println!("Script timed out after {:?}", duration);
        }
        Err(Error::OpenScriptNotFound) => {
            println!("Script interpreter not found in PATH");
        }
        Err(e) => {
            println!("Execution error: {}", e);
        }
    }
}
```

### Using Macros

```rust
use openrunner_rs::{run_script, spawn_script, ScriptOptions};

#[tokio::main]
async fn main() -> openrunner_rs::Result<()> {
    let options = ScriptOptions::new().openscript_path("/bin/sh");
    
    // Quick execution with macro
    let result = run_script!("echo 'Hello from macro!'", options).await?;
    println!("Macro result: {}", result.stdout);
    
    // Spawn with macro
    let spawn_result = spawn_script!("sleep 5 && echo 'Background task'", options).await?;
    let output = spawn_result.child.wait_with_output().await?;
    println!("Background result: {}", String::from_utf8_lossy(&output.stdout));
    
    Ok(())
}
```

## ๐Ÿงช Testing

Run the test suite:

```bash
# Run all tests
cargo test

# Run with output
cargo test -- --nocapture

# Run integration tests only
cargo test --test integration_test

# Run with coverage
cargo install cargo-tarpaulin
cargo tarpaulin --out Html
```

## ๐Ÿณ Docker Support

Build and test in Docker:

```bash
# Build the container
docker build -t openrunner-rs .

# Run tests in container
docker run openrunner-rs

# Interactive development
docker run -it -v $(pwd):/workspace openrunner-rs bash
```

## ๐Ÿ”ง Development

### Prerequisites

- Rust 1.70+ 
- tokio runtime
- Shell interpreter (bash, sh, etc.)

### Building

```bash
# Debug build
cargo build

# Release build
cargo build --release

# Run examples
cargo run --example basic
cargo run --example advanced

# Generate documentation
cargo doc --open
```

### Linting

```bash
# Format code
cargo fmt

# Run clippy
cargo clippy -- -D warnings

# Full lint check
make lint
```

## ๐Ÿ“ License

This project is dual-licensed under either:

- [MIT License]LICENSE-MIT
- [Apache License 2.0]LICENSE-APACHE

at your option.

## ๐Ÿค Contributing

Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our development process, how to propose bugfixes and improvements.

## ๐Ÿ“ž Support

- ๐Ÿ“– [Documentation]https://docs.rs/openrunner-rs
- ๐Ÿ› [Issue Tracker]https://github.com/openrunner-dev/openrunner-rs/issues
- ๐Ÿ’ฌ [Discussions]https://github.com/openrunner-dev/openrunner-rs/discussions