# treesap
_A `#[derive(Parser)]` interface for [sap](https://crates.io/crates/sap)_
[](https://crates.io/crates/treesap)
[](https://docs.rs/treesap)
[](../LICENSE)
treesap is a companion crate to [sap](https://crates.io/crates/sap) that lets
you declare your CLI arguments as a plain Rust struct instead of writing a
manual parsing loop. Annotate your struct with `#[derive(Parser)]` and treesap
generates a `parse()` method that calls into `sap` under the hood.
> **treesap is a work in progress.** See the [Status](#status) section for a
> precise breakdown of what is implemented today versus what is planned. For
> production-grade argument parsing, consider using
> [sap](https://crates.io/crates/sap) directly until treesap reaches a stable
> feature set.
## Status
| `#[derive(Parser)]` on structs | Implemented |
| `bool` fields as long flags | Implemented |
| Short flag support (`-v`) | Planned |
| Value-taking fields (`String`, `u32`, …) | Planned |
| `Option<T>` fields (optional arguments) | Planned |
| Required vs optional field distinction | Planned |
| `--help` auto-generation | Planned |
| `--version` auto-generation | Planned |
| Subcommands | Planned |
## Installation
Add both `sap` and `treesap` to your `Cargo.toml`:
```toml
[dependencies]
sap = "0.2.0"
treesap = "0.1.0"
```
## Quick Start
Declare your arguments as a struct and derive `Parser`. Each `bool` field
becomes a long flag whose name matches the field name exactly:
```rust
use treesap::Parser;
#[derive(Debug, Parser)]
struct Args {
verbose: bool,
dry_run: bool,
}
fn main() -> sap::Result<()> {
let args = Args::parse()?;
if args.verbose {
println!("verbose mode enabled");
}
if args.dry_run {
println!("dry-run: no changes will be made");
}
Ok(())
}
```
Running the program:
```
$ myprogram --verbose
verbose mode enabled
$ myprogram --verbose --dry-run
verbose mode enabled
dry-run: no changes will be made
```
### What `parse()` does
The generated `parse()` method:
1. Calls `sap::Parser::from_env()` to read `std::env::args()`.
2. Iterates over all arguments with `forward()`.
3. Matches each `Argument::Long(name)` against the field names in the struct.
4. Returns `Err` for any unrecognised argument via
`sap::Argument::unexpected()`.
5. Returns `Ok(Self { … })` once all arguments are consumed.
Because `parse()` is generated by a proc-macro, the struct must be defined in a
context where `sap` is also a direct dependency of your crate.
### Current limitations
- Only `bool` fields are supported. Any other field type will compile but
produce incorrect behaviour (all fields are hardcoded to `true` when their
flag appears).
- `#[arg(…)]` and `#[command(…)]` attributes are accepted by the macro but not
yet acted upon; short flags, custom flag names, and descriptions are silently
ignored.
- Fields are initialised with `.unwrap()`, so a missing flag causes a panic
rather than a clean error. Make all fields optional by providing defaults
until required-field handling is implemented.
- Enums and unions are not supported (`todo!()` will panic at compile time).
## Relationship to sap
treesap is a thin layer on top of [sap](https://crates.io/crates/sap). The
generated `parse()` function is equivalent to what you would write by hand using
`sap::Parser`. If treesap does not yet support a feature you need, you can drop
down to `sap` directly:
```rust
use sap::{Parser, Argument};
fn main() -> sap::Result<()> {
let mut parser = Parser::from_env()?;
let mut verbose = false;
let mut output = None;
while let Some(arg) = parser.forward()? {
match arg {
Argument::Short('v') | Argument::Long("verbose") => verbose = true,
Argument::Short('o') | Argument::Long("output") => {
output = parser.value()?;
}
unknown => return Err(unknown.unexpected()),
}
}
println!("verbose={verbose}, output={output:?}");
Ok(())
}
```
See the [sap README](../README.md) and [sap documentation](https://docs.rs/sap)
for the full API reference.
## License
This project is licensed under the
[Apache-2.0 License](http://www.apache.org/licenses/LICENSE-2.0). For more
information, please see the [LICENSE](../LICENSE) file.