diff --git a/docs/src/boxed/cases.txt b/docs/src/boxed/cases.txt deleted file mode 100644 index 5a8a9fb7..00000000 --- a/docs/src/boxed/cases.txt +++ /dev/null @@ -1,10 +0,0 @@ -? You can make some dynamic things with bpaf, depending on a value of `sneaky` command `a` is -? either enabled or disabled, here it's false so the only available command is `b` -> b hello -OK -B("hello") - -? command `a` should be missing -> a hello -Stderr -No such command: `a`, did you mean `b`? diff --git a/docs2/src/boxed/cases.md b/docs2/src/boxed/cases.md new file mode 100644 index 00000000..ac757c71 --- /dev/null +++ b/docs2/src/boxed/cases.md @@ -0,0 +1,10 @@ +It is also possible to make dynamic choice about the parsers. This example defines two parsers +for distance - imperial and metric and picks one from some source available at runtime only. + +Help message will contain only one parser + +> --help + +and only one parser will produce a result + +> --distance 10 diff --git a/docs2/src/boxed/combine.rs b/docs2/src/boxed/combine.rs new file mode 100644 index 00000000..6b01d2ec --- /dev/null +++ b/docs2/src/boxed/combine.rs @@ -0,0 +1,26 @@ +// +use bpaf::*; + +pub fn options() -> OptionParser { + let miles = long("distance") + .help("distance in miles") + .argument::("MILES") + .map(|d| d * 1.609344); + + let km = long("distance") + .help("distance in km") + .argument::("KM"); + + // suppose this is reading from config fule + let use_metric = true; + + // without use of `boxed` here branches have different types so it won't typecheck + // boxed make it so branches have the same type as long as they return the same type + let distance = if use_metric { + km.boxed() + } else { + miles.boxed() + }; + + distance.to_options() +} diff --git a/docs2/src/numeric_prefix/cases.md b/docs2/src/numeric_prefix/cases.md new file mode 100644 index 00000000..9ef59683 --- /dev/null +++ b/docs2/src/numeric_prefix/cases.md @@ -0,0 +1,16 @@ +If `bpaf` can parse first positional argument as number - it becomes a numeric prefix + +> 10 eat + +Otherwise it gets ignored + +> "just eat" + + +If validation passes but second argument is missing - in this example there's no fallback + +> 10 + +Help should show that the prefix is optional + +> --help diff --git a/documentation/_documentation/_2_howto/_07_skip_positional/index.md b/documentation/_documentation/_2_howto/_07_skip_positional/index.md index ecc718ab..bd6b466f 100644 --- a/documentation/_documentation/_2_howto/_07_skip_positional/index.md +++ b/documentation/_documentation/_2_howto/_07_skip_positional/index.md @@ -1,3 +1,7 @@ #### Skipping optional positional items if parsing or validation fails -#![cfg_attr(not(doctest), doc = include_str!("docs/numeric_prefix.md"))] +Combinations like [`Parser::optional`] and +[`ParseOptional::catch`](crate::parsers::ParseOptional::catch) allow to try to parse something +and then handle the error as if pase attempt never existed + +#![cfg_attr(not(doctest), doc = include_str!("docs2/numeric_prefix.md"))] diff --git a/documentation/_documentation/_2_howto/_08_cargo_helper/index.md b/documentation/_documentation/_2_howto/_08_cargo_helper/index.md index 666851a4..45917b51 100644 --- a/documentation/_documentation/_2_howto/_08_cargo_helper/index.md +++ b/documentation/_documentation/_2_howto/_08_cargo_helper/index.md @@ -1,5 +1,6 @@ #### Implementing cargo commands -With [`cargo_helper`](crate::batteries::cargo_helper) you can use your application as a `cargo` command +With [`cargo_helper`](crate::batteries::cargo_helper) you can use your application as a `cargo` command. +You will need to enable `batteries` feature while importing `bpaf`. -#![cfg_attr(not(doctest), doc = include_str!("docs/cargo_helper.md"))] +#![cfg_attr(not(doctest), doc = include_str!("docs2/cargo_helper.md"))] diff --git a/src/_documentation.rs b/src/_documentation.rs index 1ead2bae..571abae8 100644 --- a/src/_documentation.rs +++ b/src/_documentation.rs @@ -2463,7 +2463,11 @@ //! //! #### Skipping optional positional items if parsing or validation fails //! - #![cfg_attr(not(doctest), doc = include_str!("docs/numeric_prefix.md"))] + //! Combinations like [`Parser::optional`] and + //! [`ParseOptional::catch`](crate::parsers::ParseOptional::catch) allow to try to parse something + //! and then handle the error as if pase attempt never existed + //! + #![cfg_attr(not(doctest), doc = include_str!("docs2/numeric_prefix.md"))] //! //! //!   @@ -2511,9 +2515,10 @@ //! //! #### Implementing cargo commands //! - //! With [`cargo_helper`](crate::batteries::cargo_helper) you can use your application as a `cargo` command + //! With [`cargo_helper`](crate::batteries::cargo_helper) you can use your application as a `cargo` command. + //! You will need to enable `batteries` feature while importing `bpaf`. //! - #![cfg_attr(not(doctest), doc = include_str!("docs/cargo_helper.md"))] + #![cfg_attr(not(doctest), doc = include_str!("docs2/cargo_helper.md"))] //! //! //!   diff --git a/src/docs/anywhere.md b/src/docs/anywhere.md deleted file mode 100644 index 01e3b9c2..00000000 --- a/src/docs/anywhere.md +++ /dev/null @@ -1,83 +0,0 @@ -
-Combinatoric usage - -```no_run -# use bpaf::*; -#[derive(Debug, Clone)] -# #[allow(dead_code)] -pub struct Options { - turbo: bool, - backing: bool, - xinerama: bool, -} - -fn toggle_option(name: &'static str, help: &'static str) -> impl Parser { - any::(name, move |s: String| { - if let Some(rest) = s.strip_prefix('+') { - (rest == name).then_some(true) - } else if let Some(rest) = s.strip_prefix('-') { - (rest == name).then_some(false) - } else { - None - } - }) - .help(help) - .anywhere() -} - -pub fn options() -> OptionParser { - let backing = toggle_option("backing", "Backing status").fallback(false); - let xinerama = toggle_option("xinerama", "Xinerama status").fallback(true); - let turbo = short('t') - .long("turbo") - .help("Engage the turbo mode") - .switch(); - construct!(Options { - turbo, - backing, - xinerama, - }) - .to_options() -} -``` - -
-
-Examples - - -This example shows how to parse some very unusual options, same style as used by Xorg -`-backing` disables backing `+backing` enables it, usual restrictions and combinations apply: -fails if present more than once, can be further transformed with combinators. -By default `xinerama` is enabled, anything else is disabled -```console -% app -Options { turbo: false, backing: false, xinerama: true } -``` - -Strange things we added can be mixed with the regular options -```console -% app --turbo +backing -xinerama -Options { turbo: true, backing: true, xinerama: false } -``` - -As expected - order doesn't matter -```console -% app +backing --turbo -Options { turbo: true, backing: true, xinerama: true } -``` - ---help will try to render it but you can always `.hide` it and add your own lines -with `.header` or `.footer` methods on `OptionParser`. -```console -% app --help -Usage: [-t] [] [] - -Available options: - -t, --turbo Engage the turbo mode - Backing status - Xinerama status - -h, --help Prints help information -``` - -
diff --git a/src/docs/anywhere_1.md b/src/docs/anywhere_1.md deleted file mode 100644 index 84ce2c9f..00000000 --- a/src/docs/anywhere_1.md +++ /dev/null @@ -1,88 +0,0 @@ -
-Combinatoric usage - -```no_run -# use bpaf::*; -#[derive(Debug, Clone)] -# #[allow(dead_code)] -pub struct Options { - multi_arg: Option, - turbo: bool, -} - -#[derive(Debug, Clone)] -# #[allow(dead_code)] -pub struct MultiArg { - set: (), - name: String, - value: String, -} - -pub fn options() -> OptionParser { - let set = long("set").req_flag(()); - let name = positional("NAME").help("Name for the option"); - let value = positional("VAL").help("Value to set"); - let multi_arg = construct!(MultiArg { set, name, value }) - .adjacent() - .optional(); - - let turbo = long("turbo").switch(); - construct!(Options { multi_arg, turbo }).to_options() -} -``` - -
-
-Derive usage - -```no_run -# use bpaf::*; -#[derive(Debug, Clone, Bpaf)] -# #[allow(dead_code)] -#[bpaf(options)] -pub struct Options { - #[bpaf(external, optional)] - multi_arg: Option, - turbo: bool, -} - -#[derive(Debug, Clone, Bpaf)] -#[bpaf(adjacent)] -# #[allow(dead_code)] -pub struct MultiArg { - #[bpaf(long)] - set: (), - #[bpaf(positional("NAME"))] - /// Name for the option - name: String, - #[bpaf(positional("VAL"))] - /// Value to set - value: String, -} -``` - -
-
-Examples - - -It's possible to implement multi argument options by using required flag followed by one or -more positional items -```console -% app --turbo --set name Bob -Options { multi_arg: Some(MultiArg { set: (), name: "name", value: "Bob" }), turbo: true } -``` - -Other flags can go on either side of items -```console -% app --set name Bob --turbo -Options { multi_arg: Some(MultiArg { set: (), name: "name", value: "Bob" }), turbo: true } -``` - -But not in between -```console -% app --set name --turbo Bob -Expected , got "--turbo". Pass --help for usage information -``` - -
diff --git a/src/docs/boxed.md b/src/docs/boxed.md deleted file mode 100644 index 535263f5..00000000 --- a/src/docs/boxed.md +++ /dev/null @@ -1,51 +0,0 @@ -
-Combinatoric usage - -```no_run -# use bpaf::*; -# #[allow(dead_code)] -#[derive(Debug, Clone)] -pub enum Command { - A(String), - B(String), -} - -pub fn options() -> OptionParser { - let a = positional::("A") - .map(Command::A) - .to_options() - .command("a"); - let b = positional::("B") - .map(Command::B) - .to_options() - .command("b"); - let sneaky = false; - let a = if sneaky { - construct!(a) - } else { - let f = fail("No such command: `a`, did you mean `b`?"); - construct!(f) - }; - construct!([a, b]).to_options() -} -``` - -
-
-Examples - - -You can make some dynamic things with bpaf, depending on a value of `sneaky` command `a` is -either enabled or disabled, here it's false so the only available command is `b` -```console -% app b hello -B("hello") -``` - -command `a` should be missing -```console -% app a hello -No such command: `a`, did you mean `b`? -``` - -
diff --git a/src/docs/cargo_helper.md b/src/docs/cargo_helper.md deleted file mode 100644 index 9757b3f0..00000000 --- a/src/docs/cargo_helper.md +++ /dev/null @@ -1,77 +0,0 @@ -
-Combinatoric usage - -```no_run -# use bpaf::*; -# #[allow(dead_code)] -#[derive(Debug, Clone)] -pub struct Options { - argument: usize, - switch: bool, -} - -pub fn options() -> OptionParser { - let argument = long("argument") - .help("An argument") - .argument::("ARG"); - let switch = short('s').help("A switch").switch(); - let options = construct!(Options { argument, switch }); - - cargo_helper("pretty", options).to_options() -} -``` - -
-
-Derive usage - -```no_run -# use bpaf::*; -# #[allow(dead_code)] -#[derive(Debug, Clone, Bpaf)] -#[bpaf(options("pretty"))] -pub struct Options { - /// An argument - argument: usize, - /// A switch - #[bpaf(short)] - switch: bool, -} -``` - -
-
-Examples - - -Let's say the goal is to parse an argument and a switch: -```console -% app --argument 15 -Options { argument: 15, switch: false } -``` - -But when used as a `cargo` subcommand, cargo will also pass the command name, this example -uses _wrong_ subcommand name to bypass the helper and show how it would look without it -```console -% app wrong --argument 15 -No such command or positional: `wrong`, did you mean `--argument`? -``` - -When used with the right command - helper simply consumes it -```console -% app pretty --argument 42 -s -Options { argument: 42, switch: true } -``` - -And it doesn't show up in `--help` so not to confuse users -```console -% app --help -Usage: --argument ARG [-s] - -Available options: - --argument An argument - -s A switch - -h, --help Prints help information -``` - -
diff --git a/src/docs/numeric_prefix.md b/src/docs/numeric_prefix.md deleted file mode 100644 index 7070b064..00000000 --- a/src/docs/numeric_prefix.md +++ /dev/null @@ -1,61 +0,0 @@ -```no_run -use bpaf::*; - -#[derive(Debug, Clone)] -#[allow(dead_code)] -pub struct Options { - prefix: Option, - command: String, -} - -pub fn options() -> OptionParser { - let prefix = positional::("PREFIX") - .help("Optional numeric command prefix") - .optional() - .catch(); - let command = positional::("COMMAND").help("Required command name"); - - construct!(Options { prefix, command }).to_options() -} - -fn main() { - println!("{:#?}", options().run()); -} - -``` -
-Examples - - -If `bpaf` can parse first positional argument as number - it becomes a numeric prefix -```console -% app 10 eat -Options { prefix: Some(10), command: "eat" } -``` - -Otherwise it gets ignored -```console -% app "just eat" -Options { prefix: None, command: "just eat" } -``` - -If validation passes but second argument is missing - there's no fallback -```console -% app 10 -Expected , pass --help for usage information -``` - -Help should reflect the fact that the prefix is optional -```console -% app --help -Usage: [] - -Available positional items: - Optional numeric command prefix - Required command name - -Available options: - -h, --help Prints help information -``` - -
diff --git a/src/docs2/boxed.md b/src/docs2/boxed.md new file mode 100644 index 00000000..9c56fe73 --- /dev/null +++ b/src/docs2/boxed.md @@ -0,0 +1,73 @@ + +```no_run + +pub fn options() -> OptionParser { + let miles = long("distance") + .help("distance in miles") + .argument::("MILES") + .map(|d| d * 1.609344); + + let km = long("distance") + .help("distance in km") + .argument::("KM"); + + // suppose this is reading from config fule + let use_metric = true; + + // without use of `boxed` here branches have different types so it won't typecheck + // boxed make it so branches have the same type as long as they return the same type + let distance = if use_metric { + km.boxed() + } else { + miles.boxed() + }; + + distance.to_options() +} + +fn main() { + println!("{:?}", options().run()) +} +``` + +
Output + +It is also possible to make dynamic choice about the parsers. This example defines two parsers +for distance - imperial and metric and picks one from some source available at runtime only. + +Help message will contain only one parser + + +
+$ app --help
+

Usage: app --distance=KM

+Available options:
--distance=KM
+
distance in km
+
-h, --help
+
Prints help information
+
+

+ +
+ + +and only one parser will produce a result + + +
+$ app --distance 10
+10.0 +
+ +
\ No newline at end of file diff --git a/src/docs2/numeric_prefix.md b/src/docs2/numeric_prefix.md new file mode 100644 index 00000000..1c37d353 --- /dev/null +++ b/src/docs2/numeric_prefix.md @@ -0,0 +1,114 @@ +
examples/numeric_prefix.rs + +```no_run +/// You can parse multiple positional elements with earlier being optional as well +/// This example takes two - optional numeric prefix and a command name: +/// +/// > numeric_prefix 8 work +/// Options { prefix: Some(8), command: "work" } +/// +/// > numeric_prefix sleep +/// Options { prefix: None, command: "sleep" } +/// +/// Generated usage reflects that: +/// Usage: numeric_prefix [PREFIX] COMMAND +use bpaf::*; + +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub struct Options { + prefix: Option, + command: String, +} + +pub fn options() -> OptionParser { + let prefix = positional::("PREFIX") + .help("Optional numeric command prefix") + .optional() + .catch(); + let command = positional::("COMMAND").help("Required command name"); + + construct!(Options { prefix, command }).to_options() +} + +fn main() { + println!("{:#?}", options().run()); +} + +``` + +
+ +
Output + +If `bpaf` can parse first positional argument as number - it becomes a numeric prefix + + +
+$ app 10 eat
+Options { prefix: Some(10), command: "eat" } +
+ + +Otherwise it gets ignored + + +
+$ app "just eat"
+Options { prefix: None, command: "just eat" } +
+ + + +If validation passes but second argument is missing - in this example there's no fallback + + +
+$ app 10
+Error: expected COMMAND, pass --help for usage information + +
+ + +Help should show that the prefix is optional + + +
+$ app --help
+

Usage: app [PREFIX] COMMAND

+Available positional items:
PREFIX
+
Optional numeric command prefix
+
COMMAND
+
Required command name
+
+

+Available options:
-h, --help
+
Prints help information
+
+

+ +
+ +
\ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 4e83a866..9774669a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1238,7 +1238,7 @@ pub trait Parser { /// different parsers in different conditional branches /// /// You can create it with a single argument `construct` macro or by using `boxed` annotation - #[doc = include_str!("docs/boxed.md")] + #[cfg_attr(not(doctest), doc = include_str!("docs2/boxed.md"))] fn boxed(self) -> Box> where Self: Sized + Parser + 'static,