Document (and possibly improve) how to create commands without options with the combinatoric API
Hi! This is my first time using this library, and I find it pretty cool and useful. The docs are great, too — although I wish the library was a little bit more self-explanatory, but you can't have it all.
Anyway, I've spent quite some time reading docs to no avail, searching for issues and discussions, experimenting with code, and was in the middle of writing a discussion post of my own when I finally realized how to make a command parser without any options of its own using the combinatoric API.
The first solution was to have a pure value as the only option, like so:
enum Command {
Run { _placeholder: () },
// ...
}
fn run() -> impl bpaf::Parser<Command> {
let _placeholder = bpaf::pure(());
bpaf::construct!(Command::Run { _placeholder })
.to_options()
.descr("Run something")
.command("run")
}
This made it work, but I still had to destructure a useless value when using it:
match inv.command {
cmd::Command::Run { _placeholder } => {}
// ...
}
However the real solution was much closer to what I tried naturally, just needed somewhat unexpected changes:
#[derive(Clone)]
enum Command {
Run,
// ...
}
fn run() -> impl bpaf::Parser<Command> {
bpaf::construct!(Command::Run {})
.to_options()
.descr("Run something")
.command("run")
}
The first unexpected thing is that the construct! macro didn't work with just Command::Run — failing with "unexpected token in input" from rust-analyzer, and this build error:
error: no rules expected `::`
--> src/cmd.rs:50:26
|
50 | bpaf::construct!(Command::Run)
| ^^ no rules expected this token in macro call
|
note: while trying to match `(`
--> /home/mks/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bpaf-0.9.19/src/lib.rs:418:52
|
418 | (@prepare $ty:tt [$($fields:tt)*] $field:ident () $(, $($rest:tt)*)? ) => {{
| ^
So I had to add empty curly braces {} which gave me a different error that suggested adding #[derive(Clone)] to the Command enum.
Regular error on `.to_options()` suggesting I derive Clone on my enum
error[E0599]: the method `to_options` exists for struct `ParsePure<Command>`, but its trait bounds were not satisfied
--> src/cmd.rs:48:4
|
8 | pub enum Command {
| ---------------- doesn't satisfy `cmd::Command: Clone`
...
47 | / bpaf::construct!(Command::Run {})
48 | | .to_options()
| | -^^^^^^^^^^ method cannot be called on `ParsePure<Command>` due to unsatisfied trait bounds
| |_________|
|
|
::: /home/mks/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bpaf-0.9.19/src/structs.rs:852:1
|
852 | pub struct ParsePure<T>(pub(crate) T);
| ----------------------- doesn't satisfy `_: Parser<Command>`
|
= note: the following trait bounds were not satisfied:
`cmd::Command: Clone`
which is required by `bpaf::structs::ParsePure<cmd::Command>: Parser<cmd::Command>`
help: consider annotating `cmd::Command` with `#[derive(Clone)]`
|
8 + #[derive(Clone)]
9 | pub enum Command {
|
And adding the derive made it finally work!
So, I think it would be really cool to add documentation on how to properly do this to the appropriate tutorial page, and the command method.
Bonus points if the library could be made to support the proper syntax to construct empty enum variants (remove the need for curly braces).
P.S. Now I know that all struct members must implement Clone, I just didn't notice it before since most things implement it, and I didn't need to use pure values of enums.
P.P.S. I did find a discussion which showed how to achieve the same setup with the derive API, and it seems much more self-explanatory (whether it is documented or not — I haven't checked).
So... There's nothing special about construct! macro. All it does is taking several parsers and giving you something that is impl Parser<Foo>. You can use .to_options().command("foo") on any other parser - and you almost got it yourself! Just make whatever you actually want with pure and use that.
use bpaf::*;
#[derive(Debug, Clone, Copy)]
enum Commands {
Unus,
}
fn main() {
let unus = pure(Commands::Unus).to_options().command("unus");
let parser = unus.to_options();
println!("{:?}", parser.run());
}
But you are right, I'll update the construct! macro to work with field-less enums and structs - this behavior totally makes sense,