248 lines
5.3 KiB
Markdown
248 lines
5.3 KiB
Markdown
# 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.3.1"
|
|
```
|
|
|
|
## 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""#);
|
|
```
|
|
|
|
#### 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).
|