| Crates.io | clappen |
| lib.rs | clappen |
| version | 0.1.3 |
| created_at | 2024-12-09 17:25:30.468565+00 |
| updated_at | 2024-12-14 12:20:01.039613+00 |
| description | Clap flatten prefix macro |
| homepage | |
| repository | https://github.com/killzoner/clappen |
| max_upload_size | |
| id | 1477691 |
| size | 134,190 |
Flatten prefix for
clap
Integrate flatten prefix in your clap parsers easily.
For more details, see:
use clap::Parser;
// export is the name of the macro generated
#[clappen::clappen(export = nested)]
// this mod definition does not appear in the
// final generated code, it's just a convenient
// wrapper for item definitions
mod unused_mod {
#[derive(clap::Args, Debug, Clone)]
pub struct Remote {
#[arg(env, long)]
id: String,
}
impl Remote {
fn a_function(&self) -> String {
// this `self` reference will be rewritten
// to field prefixed with `test`, ie `self.test_id`.
format!("id: {:?}", self.id)
}
}
}
// export is the name of the macro generated
#[clappen::clappen(export = prefixed_struct_generator)]
// mod definition not used too
mod unused_mod {
#[derive(clap::Parser, Debug, Clone)]
#[command(version, about)]
pub struct Options {
#[arg(env, long)]
url: String,
#[command(flatten)]
// `apply` is the macro export used
// (defined with `Remote` struct earlier)
// `prefix` is optional
#[clappen_command(apply = nested, prefix = "test")]
nested: Remote,
}
}
// generate the default struct without prefix
prefixed_struct_generator!();
fn main() -> Result<(), Box<dyn std::error::Error>> {
let _ = Options::parse();
Ok(())
}
clap unfortunately doesn't preserve field structure and prefix when flattening commands.
See this issue for more in-depth explanation.
This crate allows fixing that while not requiring custom wrapper to the end clap parser used. It's just a struct macro generator that uses clap default behaviour around arg attribute.
See clap documentation for arg.
Providing custom long or env to your fields is not supported. See Roadmap.
References to fields with more than 1 nesting level won't work - like self.my_field_level1.my_field_level2.
Generally this should not be needed because you want to get something out of your reusable struct parser (and Rust might get in your way for borrow-related things).
If you still want to do that, you can write custom getter functions, and the renamed fields will be picked in the impl blocks.
Probably some edge cases are not covered. For example, clap subcommands should be working, but it was not tested extensively as my use case is mainly reusing structs in a mono repository fashion.
long / env prefix injection.clap tighter and the code more complicated though.clappen_command required with command(flatten) even without prefix ?Because people work with copy/paste.
For example, if this was not required, you might write this
#[clappen::clappen(export = nested)]
mod m {
#[derive(clap::Args)]
pub struct Nested {}
}
#[clappen::clappen(export = copyable_opts)]
mod m {
#[derive(clap::Parser)]
#[command(version, about)]
pub struct CopyableOptions {
#[command(flatten)]
nested: Nested,
}
}
But then if you copy paste this and turn it to a reusable parser, you get this
#[clappen::clappen(export = nested)]
mod m {
#[derive(clap::Args)]
pub struct Nested {}
}
#[clappen::clappen(export = copyable_opts)]
mod m {
#[derive(clap::Args)]
pub struct CopyableOptions {
#[command(flatten)]
nested: Nested,
}
}
#[clappen::clappen(export = parser)]
mod m {
#[derive(clap::Parser)]
#[command(version, about)]
pub struct Options {
#[command(flatten)]
// You're back to original issue, prefix is
// not maintained because `clappen` was not provided
opts: CopyableOptions,
}
}
Making clappen_command required for all flatten items avoids having to think about that when refactoring, you know that your prefix will be maintained even when using a single struct without a prefix.
syn, quote and paste.paste but it is sadly now unmaintained.clap and all its great features.