comlexr 1.4.2

Dynamically build Command objects with conditional expressions
Documentation
# comlexr

`comlexr` is a Rust procedural macro crate designed to simplify command expression generation by using flexible syntax constructs. It allows you to dynamically build command-line instructions based on conditional statements, loops, pattern matching, closures, and more.

## Installation

Add `comlexr` to your project's `Cargo.toml`:

```toml
[dependencies]
comlexr = "1.4.2"
```

## MSRV
The minimum supported Rust version is `1.60.0` for broader support.

## Usage

### Basic Command Construction
Create simple command expressions using the `cmd!` macro.

```rust
use comlexr::cmd;

let command = cmd!("echo", "test");
assert_eq!(format!("{command:?}"), r#""echo" "test""#.to_string());
```

### Conditional Argument Inclusion

Use `if` statements to conditionally include arguments.

```rust
use comlexr::cmd;

let single = true;
let multi = false;

let command = cmd!(
    "echo",
    "test",
    if single => "single",
    if multi => [
        "multi",
        "arg",
    ],
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "single""#.to_string());
```

### Conditional Pattern Matching

Use `if let` syntax to conditionally include arguments based on pattern matching.

```rust
use comlexr::cmd;

let single_option = Some("single");
let multi_option: Option<&str> = None;

let command = cmd!(
    "echo",
    "test",
    if let Some(arg) = single_option => arg,
    if let Some(arg) = multi_option => [
        "multi",
        arg,
    ],
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "single""#.to_string());
```

### Iterative Argument Inclusion

Use the `for` syntax to iterate over collections and include multiple arguments.

```rust
use comlexr::cmd;

let iter = &["1", "2"];
let command = cmd!(
    "echo",
    "test",
    for iter,
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "1" "2""#.to_string());
```

### Iteration with `for in`

Leverage the `for in` syntax to map collection elements to arguments dynamically.

```rust
use comlexr::cmd;

let single_iter = &["arg1", "arg2"];
let multi_iter = &["multi1", "multi2"];

let command = cmd!(
    "echo",
    "test",
    for arg in single_iter => arg,
    for arg in multi_iter => [
        "multi",
        arg,
    ],
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "arg1" "arg2" "multi" "multi1" "multi" "multi2""#.to_string());
```

### Pattern Matching with `match`
Dynamically choose arguments based on pattern matching.

```rust
use comlexr::cmd;

enum TestArgs {
    Arg1,
    Arg2,
    Arg3,
}

let match_arg = TestArgs::Arg2;
let command = cmd!(
    "echo",
    "test",
    match match_arg {
        TestArgs::Arg1 => "arg1",
        TestArgs::Arg2 => ["arg1", "arg2"],
        TestArgs::Arg3 => ["arg1", "arg2", "arg3"],
    }
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "arg1" "arg2""#.to_string());
```

### Closures for Dynamic Argument Generation
Generate arguments on the fly using closures. The closure must return a type that implements `IntoIterator`.

```rust
use comlexr::cmd;

let arr = vec![1, 2, 3];
let input = 2;

let command = cmd!(
    "echo",
    "test",
    || arr.into_iter().map(|i| format!("{}", i * input))
);
assert_eq!(format!("{command:?}"), r#""echo" "test" "2" "4" "6""#.to_string());
```

### Set Current Directory
Specify the directory to run the command.

```rust
use comlexr::cmd;

let command = cmd!(
    cd "~/";
    "echo",
    "test",
);

assert_eq!(format!("{command:?}"), r#"cd "~/" && "echo" "test""#);
```

### Setting Environment Variables
Set environment variables for the command.

```rust
use comlexr::cmd;

const NEW_VAR: &str = "NEW_VAR";

let command = cmd!(
    env {
        "TEST": "test",
        NEW_VAR: "new_var"
    };
    "echo",
    "test",
);

assert_eq!(format!("{command:?}"), r#"NEW_VAR="new_var" TEST="test" "echo" "test""#);
```

#### Conditional
You can have a default value set for an environment variable.

```rust
use comlexr::cmd;

const NEW_VAR: &str = "NEW_VAR";
std::env::set_var("TEST", "realvalue");

let command = cmd!(
    env {
        "TEST":? "test",
        NEW_VAR: "new_var"
    };
    "echo",
    "test",
);

assert_eq!(format!("{command:?}"), r#"NEW_VAR="new_var" "echo" "test""#);
```

#### Current Directory and Env Variable Order Matters
Environment variable declarations **MUST** come after the current directory declaration.

```rust
use comlexr::cmd;

let command = cmd!(
    cd "~/";
    env {
        "TEST": "test",
    };
    "echo",
    "test",
);

assert_eq!(
    format!("{command:?}"),
    r#"cd "~/" && TEST="test" "echo" "test""#
);
```

### Piping commands
When using the `pipe` feature, you can make use of `pipe!` to chain the stdout of commands to stdin. Execution is lazy so commands aren't run until `status()` or `output()` is called.

```rust
use comlexr::{pipe, cmd};

let dir = tempfile::tempdir().unwrap();
let file = dir.path().join("out");
let mut pipe = pipe!(cmd!("echo", "test") | cmd!("tee", &file));

let status = pipe.status().unwrap();
assert!(status.success());
```

### Sending variables to stdin
You can also send data to the stdin of the first command in the chain.

```rust
use comlexr::{pipe, cmd};

let mut pipe = pipe!(stdin = "test"; cmd!("sed", "s|e|oa|"));
let output = pipe.output().unwrap();

assert!(output.status.success());
assert_eq!(String::from_utf8_lossy(&output.stdout), "toast");
```

## Features
- Conditional expressions (`if`, `if let`)
- Iteration constructs (`for`, `for in`)
- Pattern matching (`match`)
- Support for closures and dynamic expressions
- Piping stdout from one command to the stdin of another

## Examples
See the [tests](./tests/) directory for more examples on how to use `comlexr` effectively in your project.

## License
This project is licensed under the [MIT License](./LICENSE).