treesap 0.1.0

A #[derive(Parser)] interface for sap
Documentation
# treesap

_A `#[derive(Parser)]` interface for [sap](https://crates.io/crates/sap)_

[![Crates.io](https://img.shields.io/crates/v/treesap.svg)](https://crates.io/crates/treesap)
[![Documentation](https://docs.rs/treesap/badge.svg)](https://docs.rs/treesap)
[![License](https://img.shields.io/crates/l/treesap.svg)](../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

| Feature                                  | 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.