From 0771286c96900f72fe18e566bc7f779d45758da7 Mon Sep 17 00:00:00 2001 From: Michael Baikov Date: Tue, 8 Aug 2023 13:42:34 -0400 Subject: [PATCH] document tests and dynamic shell completion --- .../_2_howto/_0_testing/index.md | 43 +++ .../_2_howto/_1_completion/index.md | 49 ++++ .../_documentation/_2_howto/index.md | 8 +- .../_00_find/index.md | 0 .../{_2_howto => _3_cookbook}/_01_dd/index.md | 0 .../_02_xorg/index.md | 0 .../_03_command_chaining/index.md | 0 .../_04_multi_value/index.md | 0 .../_05_struct_groups/index.md | 0 .../_06_multi_flag/index.md | 0 .../_07_skip_positional/index.md | 0 .../_08_cargo_helper/index.md | 0 .../_09_numeric_flags/index.md | 0 .../_documentation/_3_cookbook/index.md | 7 + src/_documentation.rs | 273 ++++++++++++++++-- 15 files changed, 346 insertions(+), 34 deletions(-) create mode 100644 documentation/_documentation/_2_howto/_0_testing/index.md create mode 100644 documentation/_documentation/_2_howto/_1_completion/index.md rename documentation/_documentation/{_2_howto => _3_cookbook}/_00_find/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_01_dd/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_02_xorg/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_03_command_chaining/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_04_multi_value/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_05_struct_groups/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_06_multi_flag/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_07_skip_positional/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_08_cargo_helper/index.md (100%) rename documentation/_documentation/{_2_howto => _3_cookbook}/_09_numeric_flags/index.md (100%) create mode 100644 documentation/_documentation/_3_cookbook/index.md diff --git a/documentation/_documentation/_2_howto/_0_testing/index.md b/documentation/_documentation/_2_howto/_0_testing/index.md new file mode 100644 index 00000000..00527212 --- /dev/null +++ b/documentation/_documentation/_2_howto/_0_testing/index.md @@ -0,0 +1,43 @@ +#### Testing your parsers + +You can test values your parser produces and expected output + +```no_run +#[derive(Debug, Clone, Bpaf)] +#[bpaf(options)] +pub struct Options { + pub user: String +} + +#[test] +fn test_my_options() { + let help = options() + .run_inner(&["--help"]) + .unwrap_err() + .unwrap_stdout(); + let expected_help = "\ +Usage --user=ARG + +"; + + assert_eq!(help, expected_help); +} + +#[test] +fn test_value() { + let value = options() + .run_inner(&["--user", "Bob"]) + .unwrap(); + assert_eq!(value.user, "Bob"); +} +``` + +[`OptionParser::run_inner`] takes [`Args`] or anything that can be converted to it, in most +cases using a static slice with strings is enough. + +Easiest way to consume [`ParseFailure`] for testing purposes is with +[`ParseFailure::unwrap_stderr`] and [`ParseFailure::unwrap_stdout`] - result will lack any colors +even with them enabled which makes testing easier. + +Successful result parse produces a value, "failed" parse produces stdout or stderr outputs - +stdout to print help message or version number and stderr to print the error message. diff --git a/documentation/_documentation/_2_howto/_1_completion/index.md b/documentation/_documentation/_2_howto/_1_completion/index.md new file mode 100644 index 00000000..27b101b7 --- /dev/null +++ b/documentation/_documentation/_2_howto/_1_completion/index.md @@ -0,0 +1,49 @@ +#### Dynamic shell completion + +`bpaf` implements shell completion to allow to automatically fill in not only flag and command +names, but also argument and positional item values. + +1. Enable `autocomplete` feature: + + + ```toml + bpaf = { version = "0.9", features = ["autocomplete"] } + ``` + +2. Decorate [`argument`](crate::parsers::NamedArg::argument) and [`positional`] parsers with + [`Parser::complete`] to provide completion functions for arguments + + +3. Depending on your shell generate appropriate completion file and place it to whereever your + shell is going to look for it, name of the file should correspond in some way to name of + your program. Consult manual for your shell for the location and named conventions: + + 1. **bash** + ```console + $ your_program --bpaf-complete-style-bash >> ~/.bash_completion + ``` + + 1. **zsh**: note `_` at the beginning of the filename + ```console + $ your_program --bpaf-complete-style-zsh > ~/.zsh/_your_program + ``` + + 1. **fish** + ```console + $ your_program --bpaf-complete-style-fish > ~/.config/fish/completions/your_program.fish + ``` + + 1. **elvish** + ```console + $ your_program --bpaf-complete-style-elvish >> ~/.config/elvish/rc.elv + ``` + +4. Restart your shell - you need to done it only once or optionally after bpaf major version + upgrade: generated completion files contain only instructions how to ask your program for + possible completions and don’t change even if options are different. + + +5. Generated scripts rely on your program being accessible in $PATH + + + diff --git a/documentation/_documentation/_2_howto/index.md b/documentation/_documentation/_2_howto/index.md index ab443069..9a6fdc0f 100644 --- a/documentation/_documentation/_2_howto/index.md +++ b/documentation/_documentation/_2_howto/index.md @@ -1,7 +1 @@ -#### Parsing cookbook - - -While `bpaf`'s design tries to cover the most common use cases, mostly -[posix conventions](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html), -it can also handle some more unusual requirements. It might come at the cost of having to write -more code, more confusing error messages or worse performance, but it will get the job done. +#### HOWTO - practical, oriented to solving problems guides diff --git a/documentation/_documentation/_2_howto/_00_find/index.md b/documentation/_documentation/_3_cookbook/_00_find/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_00_find/index.md rename to documentation/_documentation/_3_cookbook/_00_find/index.md diff --git a/documentation/_documentation/_2_howto/_01_dd/index.md b/documentation/_documentation/_3_cookbook/_01_dd/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_01_dd/index.md rename to documentation/_documentation/_3_cookbook/_01_dd/index.md diff --git a/documentation/_documentation/_2_howto/_02_xorg/index.md b/documentation/_documentation/_3_cookbook/_02_xorg/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_02_xorg/index.md rename to documentation/_documentation/_3_cookbook/_02_xorg/index.md diff --git a/documentation/_documentation/_2_howto/_03_command_chaining/index.md b/documentation/_documentation/_3_cookbook/_03_command_chaining/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_03_command_chaining/index.md rename to documentation/_documentation/_3_cookbook/_03_command_chaining/index.md diff --git a/documentation/_documentation/_2_howto/_04_multi_value/index.md b/documentation/_documentation/_3_cookbook/_04_multi_value/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_04_multi_value/index.md rename to documentation/_documentation/_3_cookbook/_04_multi_value/index.md diff --git a/documentation/_documentation/_2_howto/_05_struct_groups/index.md b/documentation/_documentation/_3_cookbook/_05_struct_groups/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_05_struct_groups/index.md rename to documentation/_documentation/_3_cookbook/_05_struct_groups/index.md diff --git a/documentation/_documentation/_2_howto/_06_multi_flag/index.md b/documentation/_documentation/_3_cookbook/_06_multi_flag/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_06_multi_flag/index.md rename to documentation/_documentation/_3_cookbook/_06_multi_flag/index.md diff --git a/documentation/_documentation/_2_howto/_07_skip_positional/index.md b/documentation/_documentation/_3_cookbook/_07_skip_positional/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_07_skip_positional/index.md rename to documentation/_documentation/_3_cookbook/_07_skip_positional/index.md diff --git a/documentation/_documentation/_2_howto/_08_cargo_helper/index.md b/documentation/_documentation/_3_cookbook/_08_cargo_helper/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_08_cargo_helper/index.md rename to documentation/_documentation/_3_cookbook/_08_cargo_helper/index.md diff --git a/documentation/_documentation/_2_howto/_09_numeric_flags/index.md b/documentation/_documentation/_3_cookbook/_09_numeric_flags/index.md similarity index 100% rename from documentation/_documentation/_2_howto/_09_numeric_flags/index.md rename to documentation/_documentation/_3_cookbook/_09_numeric_flags/index.md diff --git a/documentation/_documentation/_3_cookbook/index.md b/documentation/_documentation/_3_cookbook/index.md new file mode 100644 index 00000000..98dbd20e --- /dev/null +++ b/documentation/_documentation/_3_cookbook/index.md @@ -0,0 +1,7 @@ +#### Parsing cookbook +How to parse less frequent combinations + +While `bpaf`'s design tries to cover the most common use cases, mostly +[posix conventions](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html), +it can also handle some more unusual requirements. It might come at the cost of having to write +more code, more confusing error messages or worse performance, but it will get the job done. diff --git a/src/_documentation.rs b/src/_documentation.rs index 0d4c6f18..c05a4b78 100644 --- a/src/_documentation.rs +++ b/src/_documentation.rs @@ -4,7 +4,8 @@ //! //! - [Introduction and design goals](_0_intro) - A quick intro. What, why and how //! - [Tutorials](_1_tutorials) - practical, learning oriented guides -//! - [Parsing cookbook](_2_howto) +//! - [HOWTO - practical, oriented to solving problems guides](_2_howto) +//! - [Parsing cookbook](_3_cookbook) - How to parse less frequent combinations //! - [Theory explanation](_4_explanation) - Theoretical information about abstractions used by the library, oriented for understanding //! pub mod _0_intro { @@ -140,7 +141,7 @@ //! //! //! - //! [Parsing cookbook →](super::_2_howto) + //! [HOWTO - practical, oriented to solving problems guides →](super::_2_howto) //! //! //! @@ -168,7 +169,7 @@ //! //! //! - //! [Parsing cookbook →](super::_2_howto) + //! [HOWTO - practical, oriented to solving problems guides →](super::_2_howto) //! //! //! @@ -2068,13 +2069,231 @@ //! //! //! + //! [Parsing cookbook →](super::_3_cookbook) + //! + //! + //! + //! + //! #### HOWTO - practical, oriented to solving problems guides + //! + //! - [Testing your parsers](_0_testing) + //! - [Dynamic shell completion](_1_completion) + //! + //!   + //! + //! + //! + //! + //! + //!
+ //! + //! [← Tutorials](super::_1_tutorials) + //! + //! + //! + //! [↑ Project documentation ↑](super::super::_documentation) + //! + //! + //! + //! [Parsing cookbook →](super::_3_cookbook) + //! + //!
+ //! + pub mod _0_testing { + //!   + //! + //! + //! + //! + //! + //!
+ //! + //! + //! [↑ HOWTO - practical, oriented to solving problems guides ↑](super::super::_2_howto) + //! + //! + //! + //! [Dynamic shell completion →](super::_1_completion) + //! + //!
+ //! + //! #### Testing your parsers + //! + //! You can test values your parser produces and expected output + //! + //! ```no_run + //! #[derive(Debug, Clone, Bpaf)] + //! #[bpaf(options)] + //! pub struct Options { + //! pub user: String + //! } + //! + //! #[test] + //! fn test_my_options() { + //! let help = options() + //! .run_inner(&["--help"]) + //! .unwrap_err() + //! .unwrap_stdout(); + //! let expected_help = "\ + //! Usage --user=ARG + //! + //! "; + //! + //! assert_eq!(help, expected_help); + //! } + //! + //! #[test] + //! fn test_value() { + //! let value = options() + //! .run_inner(&["--user", "Bob"]) + //! .unwrap(); + //! assert_eq!(value.user, "Bob"); + //! } + //! ``` + //! + //! [`OptionParser::run_inner`] takes [`Args`] or anything that can be converted to it, in most + //! cases using a static slice with strings is enough. + //! + //! Easiest way to consume [`ParseFailure`] for testing purposes is with + //! [`ParseFailure::unwrap_stderr`] and [`ParseFailure::unwrap_stdout`] - result will lack any colors + //! even with them enabled which makes testing easier. + //! + //! Successful result parse produces a value, "failed" parse produces stdout or stderr outputs - + //! stdout to print help message or version number and stderr to print the error message. + //! + //! + //!   + //! + //! + //! + //! + //! + //!
+ //! + //! + //! [↑ HOWTO - practical, oriented to solving problems guides ↑](super::super::_2_howto) + //! + //! + //! + //! [Dynamic shell completion →](super::_1_completion) + //! + //!
+ //! + use crate::*; + } + pub mod _1_completion { + //!   + //! + //! + //! + //! + //! + //!
+ //! + //! [← Testing your parsers](super::_0_testing) + //! + //! + //! + //! [↑ HOWTO - practical, oriented to solving problems guides ↑](super::super::_2_howto) + //! + //! + //!
+ //! + //! #### Dynamic shell completion + //! + //! `bpaf` implements shell completion to allow to automatically fill in not only flag and command + //! names, but also argument and positional item values. + //! + //! 1. Enable `autocomplete` feature: + //! + //! + //! ```toml + //! bpaf = { version = "0.9", features = ["autocomplete"] } + //! ``` + //! + //! 2. Decorate [`argument`](crate::parsers::NamedArg::argument) and [`positional`] parsers with + //! [`Parser::complete`] to provide completion functions for arguments + //! + //! + //! 3. Depending on your shell generate appropriate completion file and place it to whereever your + //! shell is going to look for it, name of the file should correspond in some way to name of + //! your program. Consult manual for your shell for the location and named conventions: + //! + //! 1. **bash** + //! ```console + //! $ your_program --bpaf-complete-style-bash >> ~/.bash_completion + //! ``` + //! + //! 1. **zsh**: note `_` at the beginning of the filename + //! ```console + //! $ your_program --bpaf-complete-style-zsh > ~/.zsh/_your_program + //! ``` + //! + //! 1. **fish** + //! ```console + //! $ your_program --bpaf-complete-style-fish > ~/.config/fish/completions/your_program.fish + //! ``` + //! + //! 1. **elvish** + //! ```console + //! $ your_program --bpaf-complete-style-elvish >> ~/.config/elvish/rc.elv + //! ``` + //! + //! 4. Restart your shell - you need to done it only once or optionally after bpaf major version + //! upgrade: generated completion files contain only instructions how to ask your program for + //! possible completions and don’t change even if options are different. + //! + //! + //! 5. Generated scripts rely on your program being accessible in $PATH + //! + //! + //! + //! + //! + //!   + //! + //! + //! + //! + //! + //!
+ //! + //! [← Testing your parsers](super::_0_testing) + //! + //! + //! + //! [↑ HOWTO - practical, oriented to solving problems guides ↑](super::super::_2_howto) + //! + //! + //!
+ //! + use crate::*; + } + use crate::*; + } + pub mod _3_cookbook { + //!   + //! + //! + //! + //! + //! //!
+ //! + //! [← HOWTO - practical, oriented to solving problems guides](super::_2_howto) + //! + //! + //! + //! [↑ Project documentation ↑](super::super::_documentation) + //! + //! + //! //! [Theory explanation →](super::_4_explanation) //! //!
//! //! #### Parsing cookbook - //! + //! How to parse less frequent combinations //! //! While `bpaf`'s design tries to cover the most common use cases, mostly //! [posix conventions](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html), @@ -2097,7 +2316,7 @@ //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //!
//! - //! [← Tutorials](super::_1_tutorials) + //! [← HOWTO - practical, oriented to solving problems guides](super::_2_howto) //! //! @@ -2120,7 +2339,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2164,7 +2383,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2187,7 +2406,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2225,7 +2444,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2248,7 +2467,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2284,7 +2503,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2307,7 +2526,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2336,7 +2555,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2359,7 +2578,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2387,7 +2606,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2410,7 +2629,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2454,7 +2673,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2477,7 +2696,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2505,7 +2724,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2528,7 +2747,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2557,7 +2776,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2580,7 +2799,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2608,7 +2827,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2631,7 +2850,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2681,7 +2900,7 @@ //! //! - //! [↑ Parsing cookbook ↑](super::super::_2_howto) + //! [↑ Parsing cookbook ↑](super::super::_3_cookbook) //! //! @@ -2698,7 +2917,7 @@ //! //! //!
//! - //! [← Parsing cookbook](super::_2_howto) + //! [← Parsing cookbook](super::_3_cookbook) //! //! @@ -2962,7 +3181,7 @@ //! //! //!
//! - //! [← Parsing cookbook](super::_2_howto) + //! [← Parsing cookbook](super::_3_cookbook) //! //!