Repository: pacak/bpaf
Pull request #104 (2022-10-11 14:10:59)

release 0.7.0
Other than `FromOsStr` change - it's a minor release.

# Additions
- `pure_with` implementation
   thanks to @xitep
- `FromOsStr` is replaced with magical uses of `Any` trait
- `hide_usage`
- `bright-color` and `dull-color` features
- accept fully qualified names in more places in `bpaf_derive`
- cosmetic improvements
- documentation improvements

# Migration guide 0.6.x -> 0.7.x
1. Remove FromUtf8 annotations if you have any
   ```diff
   -let coin = short('c').argument::<FromUtf8<Coin>>("COIN");
   +let coin = short('c').argument::<Coin>("COIN");
   ```
   In many cases rustc should be able to derive what the type
2. Replace `FromOsStr` implementations for your types with `FromStr`
   if you have any. If your type requires parsing `OsString` directly
   you can perform it in two steps - consuming `OsString` + parsing it
   with `Parser::parse`
3. If you want to provide your users with colored output - expose
   `bright-color` and/or `dull-color` features



Patch:
diff --git a/Cargo.toml b/Cargo.toml
index d91b87a..2a99d89 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -31,7 +31,7 @@ derive = ["bpaf_derive"]
 extradocs = []
 batteries = []
 autocomplete = []
-color = ["owo-colors"]
+color = ["owo-colors"] # used internally to switch between different code generation
 bright-color = ["color"]
 dull-color = ["color"]
 
diff --git a/Changelog.md b/Changelog.md
index 4c688bc..cc2789c 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,12 +1,14 @@
 # Change Log
 
-## bpaf [0.7.0] - unreleased
+## bpaf [0.7.0] - 2022-10-11
+- `pure_with` implementation
+   thanks to @xitep
+- `FromOsStr` is replaced with magical uses of `Any` trait
+- `hide_usage`
+- `bright-color` and `dull-color` features
+- accept fully qualified names in more places in `bpaf_derive`
 - cosmetic improvements
-- pure_with implementation
-  thanks to @xitep
-- FromOsStr is replaced with magical uses of Any trait
-- hide_usage
-- bright-color and dull-color features
+- documentation improvements
 
 # Migration guide 0.6.x -> 0.7.x
 1. Remove FromUtf8 annotations if you have any
@@ -15,17 +17,19 @@
    +let coin = short('c').argument::<Coin>("COIN");
    ```
    In many cases rustc should be able to derive what the type
-2. Replace FromOsStr implementations for your types with FromStr
-   if you have any. If your type requires parsing OsString directly
-   you can perform it in two steps - consuming OsString + parsing it
+2. Replace `FromOsStr` implementations for your types with `FromStr`
+   if you have any. If your type requires parsing `OsString` directly
+   you can perform it in two steps - consuming `OsString` + parsing it
    with `Parser::parse`
+3. If you want to provide your users with colored output - expose
+   `bright-color` and/or `dull-color` features
 
 ## bpaf [0.6.1] - 2022-09-30
 - cosmetic improvements
-- completion info in sensors example
+- completion info in `sensors` example
 - better errors in partially consumed optional items
 - better handling of -- during autcomplete
-- initial release of bpaf_cauwugo
+- initial release of `bpaf_cauwugo`
 
 ## bpaf [0.6.0] - 2022-09-22
 # What's new in 0.6.0
diff --git a/README.md b/README.md
index e534686..7a69835 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ Lightweight and flexible command line argument parser with derive and combinator
 
 ## Derive and combinatoric API
 
-`bpaf` supports both combinatoric and derive APIs and it’s possible to mix and match both APIs at once. Both APIs provide access to mostly the same features, some things are more convenient to do with derive (usually less typing), some - with combinatoric (usually maximum flexibility and reducing boilerplate structs). In most cases using just one would suffice. Whenever possible APIs share the same keywords and overall structure. Documentation for combinatoric API also explains how to perform the same action in derive style.
+`bpaf` supports both combinatoric and derive APIs and it’s possible to mix and match both APIs at once. Both APIs provide access to mostly the same features, some things are more convenient to do with derive (usually less typing), some - with combinatoric (usually maximum flexibility and reducing boilerplate structs). In most cases using just one would suffice. Whenever possible APIs share the same keywords and overall structure. Documentation is shared and contains examples for both combinatoric and derive style.
 
 `bpaf` supports dynamic shell completion for `bash`, `zsh`, `fish` and `elvish`.
 
@@ -20,114 +20,154 @@ Lightweight and flexible command line argument parser with derive and combinator
  - [Q&A][__link5]
 
 
-## Quick start, derive edition
+## Quick start - combinatoric and derive APIs
 
+<details>
+<summary style="display: list-item;">Derive style API, click to expand</summary>
  1. Add `bpaf` under `[dependencies]` in your `Cargo.toml`
+	
+	
+	```toml
+	[dependencies]
+	bpaf = { version = "0.7", features = ["derive"] }
+	```
+	
+	
+ 1. Define a structure containing command line attributes and run generated function
+	
+	
+	```rust
+	use bpaf::Bpaf;
+	
+	#[derive(Clone, Debug, Bpaf)]
+	#[bpaf(options, version)]
+	/// Accept speed and distance, print them
+	struct SpeedAndDistance {
+	    /// Speed in KPH
+	    speed: f64,
+	    /// Distance in miles
+	    distance: f64,
+	}
+	
+	fn main() {
+	    // #[derive(Bpaf)] generates `speed_and_distance` function
+	    let opts = speed_and_distance().run();
+	    println!("Options: {:?}", opts);
+	}
+	```
+	
+	
+ 1. Try to run the app
+	
+	
+	```console
+	% very_basic --help
+	Accept speed and distance, print them
+	
+	Usage: --speed ARG --distance ARG
+	
+	Available options:
+	        --speed <ARG>     Speed in KPH
+	        --distance <ARG>  Distance in miles
+	    -h, --help            Prints help information
+	    -V, --version         Prints version information
+	
+	% very_basic --speed 100
+	Expected --distance ARG, pass --help for usage information
+	
+	% very_basic --speed 100 --distance 500
+	Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
+	
+	% very_basic --version
+	Version: 0.5.0 (taken from Cargo.toml by default)
+	```
+	
+	
+ 1. You can check the [derive tutorial][__link6] for more detailed information.
+	
+	
 
-
-```toml
-[dependencies]
-bpaf = { version = "0.6", features = ["derive"] }
-```
-
- 2. Define a structure containing command line attributes and run generated function
-
-
-```rust
-use bpaf::Bpaf;
-
-#[derive(Clone, Debug, Bpaf)]
-#[bpaf(options, version)]
-/// Accept speed and distance, print them
-struct SpeedAndDistance {
-    /// Speed in KPH
-    speed: f64,
-    /// Distance in miles
-    distance: f64,
-}
-
-fn main() {
-    // #[derive(Bpaf)] generates `speed_and_distance` function
-    let opts = speed_and_distance().run();
-    println!("Options: {:?}", opts);
-}
-```
-
- 3. Try to run the app
-
-
-```console
-% very_basic --help
-Accept speed and distance, print them
-
-Usage: --speed ARG --distance ARG
-
-Available options:
-        --speed <ARG>     Speed in KPH
-        --distance <ARG>  Distance in miles
-    -h, --help            Prints help information
-    -V, --version         Prints version information
-
-% very_basic --speed 100
-Expected --distance ARG, pass --help for usage information
-
-% very_basic --speed 100 --distance 500
-Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
-
-% very_basic --version
-Version: 0.5.0 (taken from Cargo.toml by default)
-```
-
-
-## Quick start, combinatoric edition
-
+</details>
+<details>
+<summary style="display: list-item;">Combinatoric style API, click to expand</summary>
  1. Add `bpaf` under `[dependencies]` in your `Cargo.toml`
+	
+	
+	```toml
+	[dependencies]
+	bpaf = "0.7"
+	```
+	
+	
+ 1. Declare parsers for components, combine them and run it
+	
+	
+	```rust
+	use bpaf::{construct, long, Parser};
+	#[derive(Clone, Debug)]
+	struct SpeedAndDistance {
+	    /// Dpeed in KPH
+	    speed: f64,
+	    /// Distance in miles
+	    distance: f64,
+	}
+	
+	fn main() {
+	    // primitive parsers
+	    let speed = long("speed")
+	        .help("Speed in KPG")
+	        .argument::<f64>("SPEED");
+	
+	    let distance = long("distance")
+	        .help("Distance in miles")
+	        .argument::<f64>("DIST");
+	
+	    // parser containing information about both speed and distance
+	    let parser = construct!(SpeedAndDistance { speed, distance });
+	
+	    // option parser with metainformation attached
+	    let speed_and_distance
+	        = parser
+	        .to_options()
+	        .descr("Accept speed and distance, print them");
+	
+	    let opts = speed_and_distance.run();
+	    println!("Options: {:?}", opts);
+	}
+	```
+	
+	
+ 1. Try to run the app
+	
+	
+	```console
+	% very_basic --help
+	Accept speed and distance, print them
+	
+	Usage: --speed ARG --distance ARG
+	
+	Available options:
+	        --speed <ARG>     Speed in KPH
+	        --distance <ARG>  Distance in miles
+	    -h, --help            Prints help information
+	    -V, --version         Prints version information
+	
+	% very_basic --speed 100
+	Expected --distance ARG, pass --help for usage information
+	
+	% very_basic --speed 100 --distance 500
+	Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
+	
+	% very_basic --version
+	Version: 0.5.0 (taken from Cargo.toml by default)
+	```
+	
+	
+ 1. You can check the [combinatoric tutorial][__link7] for more detailed information.
+	
+	
 
-
-```toml
-[dependencies]
-bpaf = "0.6"
-```
-
- 2. Declare parsers for components, combine them and run it
-
-
-```rust
-use bpaf::{construct, long, Parser};
-#[derive(Clone, Debug)]
-struct SpeedAndDistance {
-    /// Dpeed in KPH
-    speed: f64,
-    /// Distance in miles
-    distance: f64,
-}
-
-fn main() {
-    // primitive parsers
-    let speed = long("speed")
-        .help("Speed in KPG")
-        .argument::<f64>("SPEED");
-
-    let distance = long("distance")
-        .help("Distance in miles")
-        .argument::<f64>("DIST");
-
-    // parser containing information about both speed and distance
-    let parser = construct!(SpeedAndDistance { speed, distance });
-
-    // option parser with metainformation attached
-    let speed_and_distance
-        = parser
-        .to_options()
-        .descr("Accept speed and distance, print them");
-
-    let opts = speed_and_distance.run();
-    println!("Options: {:?}", opts);
-}
-```
-
- 3. Try to run it, output should be similar to derive version
-
+</details>
 
 ## Design goals: flexibility, reusability, correctness
 
@@ -177,12 +217,12 @@ fn speed() -> impl Parser<Speed> {
 }
 ```
 
-Library follows **parse, don’t validate** approach to validation when possible. Usually you parse your values just once and get the results as a rust struct/enum with strict types rather than a stringly typed hashmap with stringly typed values in both combinatoric and derive APIs.
+Library follows **parse, don’t validate** approach to validation when possible. Usually you parse your values just once and get the results as a Rust struct/enum with strict types rather than a stringly typed hashmap with stringly typed values in both combinatoric and derive APIs.
 
 
 ## Design goals: restrictions
 
-The main restricting library sets is that you can’t use parsed values (but not the fact that parser succeeded or failed) to decide how to parse subsequent values. In other words parsers don’t have the monadic strength, only the applicative one.
+The main restricting library sets is that you can’t use parsed values (but not the fact that parser succeeded or failed) to decide how to parse subsequent values. In other words parsers don’t have the monadic strength, only the applicative one - for more detailed explanation see [Applicative functors? What is it all about][__link8].
 
 To give an example, you can implement this description:
 
@@ -198,9 +238,9 @@ But not this one:
 > 
 This set of restrictions allows `bpaf` to extract information about the structure of the computations to generate help, dynamic completion and overall results in less confusing enduser experience
 
-`bpaf` performs no parameter names validation, in fact having multiple parameters with the same name is fine and you can combine them as alternatives and performs no fallback other than [`fallback`][__link6]. You need to pay attention to the order of the alternatives inside the macro: parser that consumes the left most available argument on a command line wins, if this is the same - left most parser wins. So to parse a parameter `--test` that can be both [`switch`][__link7] and [`argument`][__link8] you should put the argument one first.
+`bpaf` performs no parameter names validation, in fact having multiple parameters with the same name is fine and you can combine them as alternatives and performs no fallback other than [`fallback`][__link9]. You need to pay attention to the order of the alternatives inside the macro: parser that consumes the left most available argument on a command line wins, if this is the same - left most parser wins. So to parse a parameter `--test` that can be both [`switch`][__link10] and [`argument`][__link11] you should put the argument one first.
 
-You must place [`positional`][__link9] items at the end of a structure in derive API or consume them as last arguments in derive API.
+You must place [`positional`][__link12] items at the end of a structure in derive API or consume them as last arguments in derive API.
 
 
 ## Dynamic shell completion
@@ -211,11 +251,11 @@ You must place [`positional`][__link9] items at the end of a structure in derive
 	
 	
 	```toml
-	bpaf = { version = "0.6.0", features = ["autocomplete"] }
+	bpaf = { version = "0.7", features = ["autocomplete"] }
 	```
 	
 	
- 1. Decorate [`argument`][__link10] and [`positional`][__link11] parsers with [`complete`][__link12] to autocomplete argument values
+ 1. Decorate [`argument`][__link13] and [`positional`][__link14] parsers with [`complete`][__link15] to autocomplete argument values
 	
 	
  1. 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:
@@ -266,7 +306,7 @@ Library aims to optimize for flexibility, reusability and compilation time over
 
 ## More examples
 
-You can find a bunch more examples here: <https://github.com/pacak/bpaf/tree/master/examples>
+You can find a more examples here: <https://github.com/pacak/bpaf/tree/master/examples>
 
 They’re usually documented or at least contain an explanation to important bits and you can see how they work by cloning the repo and running
 
@@ -278,7 +318,7 @@ $ cargo run --example example_name
 
 ## Testing your own parsers
 
-You can test your own parsers to maintain compatibility or simply checking expected output with [`run_inner`][__link14]
+You can test your own parsers to maintain compatibility or simply checking expected output with [`run_inner`][__link17]
 
 
 ```rust
@@ -327,21 +367,26 @@ Usage --user <ARG>
 	dull-color = ["bpaf/dull-color"]
 	```
 	
+	Disabled by default.
+	
 	
 
 
- [__cargo_doc2readme_dependencies_info]: ggGkYW0AYXSEG52uRQSwBdezG6GWW8ODAbr5G6KRmT_WpUB5G9hPmBcUiIp6YXKEG2QV09F__ZfbG2mHV-IJTiKrG00wy0_0pJVAG_GeoKpmAnK1YWSBgmRicGFmZTAuNi4x
- [__link0]: https://docs.rs/bpaf/0.6.1/bpaf/?search=_derive_tutorial
- [__link1]: https://docs.rs/bpaf/0.6.1/bpaf/?search=_combinatoric_tutorial
- [__link10]: https://docs.rs/bpaf/0.6.1/bpaf/?search=parsers::NamedArg::argument
- [__link11]: https://docs.rs/bpaf/0.6.1/bpaf/?search=params::positional
- [__link12]: https://docs.rs/bpaf/0.6.1/bpaf/?search=bpaf::Parser::complete
- [__link14]: https://docs.rs/bpaf/0.6.1/bpaf/?search=info::OptionParser::run_inner
- [__link2]: https://docs.rs/bpaf/0.6.1/bpaf/?search=_unusual
- [__link3]: https://docs.rs/bpaf/0.6.1/bpaf/?search=_applicative
- [__link4]: https://docs.rs/bpaf/0.6.1/bpaf/?search=batteries
+ [__cargo_doc2readme_dependencies_info]: ggGkYW0AYXSEG52uRQSwBdezG6GWW8ODAbr5G6KRmT_WpUB5G9hPmBcUiIp6YXKEG-IPQtuM4VUFG47iETJwkSENG_GD7ukhn-KMG38J41C6yQn3YWSBgmRicGFmZTAuNy4w
+ [__link0]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_derive_tutorial
+ [__link1]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_combinatoric_tutorial
+ [__link10]: https://docs.rs/bpaf/0.7.0/bpaf/?search=parsers::NamedArg::switch
+ [__link11]: https://docs.rs/bpaf/0.7.0/bpaf/?search=parsers::NamedArg::argument
+ [__link12]: https://docs.rs/bpaf/0.7.0/bpaf/?search=params::positional
+ [__link13]: https://docs.rs/bpaf/0.7.0/bpaf/?search=parsers::NamedArg::argument
+ [__link14]: https://docs.rs/bpaf/0.7.0/bpaf/?search=params::positional
+ [__link15]: https://docs.rs/bpaf/0.7.0/bpaf/?search=bpaf::Parser::complete
+ [__link17]: https://docs.rs/bpaf/0.7.0/bpaf/?search=info::OptionParser::run_inner
+ [__link2]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_unusual
+ [__link3]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_applicative
+ [__link4]: https://docs.rs/bpaf/0.7.0/bpaf/?search=batteries
  [__link5]: https://github.com/pacak/bpaf/discussions/categories/q-a
- [__link6]: https://docs.rs/bpaf/0.6.1/bpaf/?search=bpaf::Parser::fallback
- [__link7]: https://docs.rs/bpaf/0.6.1/bpaf/?search=parsers::NamedArg::switch
- [__link8]: https://docs.rs/bpaf/0.6.1/bpaf/?search=parsers::NamedArg::argument
- [__link9]: https://docs.rs/bpaf/0.6.1/bpaf/?search=params::positional
+ [__link6]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_derive_tutorial
+ [__link7]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_combinatoric_tutorial
+ [__link8]: https://docs.rs/bpaf/0.7.0/bpaf/?search=_applicative
+ [__link9]: https://docs.rs/bpaf/0.7.0/bpaf/?search=bpaf::Parser::fallback
diff --git a/bpaf_derive/src/field.rs b/bpaf_derive/src/field.rs
index fc7558a..f8b66b1 100644
--- a/bpaf_derive/src/field.rs
+++ b/bpaf_derive/src/field.rs
@@ -2,8 +2,8 @@ use proc_macro2::{Span, TokenStream};
 use quote::{quote, ToTokens};
 use syn::parse::{Parse, ParseStream};
 use syn::{
-    parenthesized, parse, Attribute, Expr, Ident, LitChar, LitStr, PathArguments, Result, Token,
-    Type, Visibility,
+    parenthesized, parse, Attribute, Expr, Ident, LitChar, LitStr, Path, PathArguments, Result,
+    Token, Type, Visibility,
 };
 
 use crate::utils::to_kebab_case;
@@ -40,14 +40,14 @@ enum ConsumerAttr {
 
 #[derive(Debug, Clone)]
 enum PostprAttr {
-    Guard(Span, Ident, Box<Expr>),
+    Guard(Span, Path, Box<Expr>),
     Many(Span, Option<LitStr>),
-    Map(Span, Ident),
+    Map(Span, Path),
     Optional(Span),
-    Parse(Span, Ident),
+    Parse(Span, Path),
     Fallback(Span, Box<Expr>),
     FallbackWith(Span, Box<Expr>),
-    Complete(Span, Ident),
+    Complete(Span, Path),
     Hide(Span),
     HideUsage(Span),
     GroupHelp(Span, Box<Expr>),
diff --git a/bpaf_derive/src/field/named_field.rs b/bpaf_derive/src/field/named_field.rs
index f803612..99e2fa8 100644
--- a/bpaf_derive/src/field/named_field.rs
+++ b/bpaf_derive/src/field/named_field.rs
@@ -3,8 +3,8 @@ use quote::{quote, ToTokens};
 use syn::parse::{Parse, ParseStream};
 use syn::spanned::Spanned;
 use syn::{
-    parenthesized, parse2, parse_quote, token, Attribute, Expr, Ident, LitChar, LitStr, Result,
-    Token, Type,
+    parenthesized, parse2, parse_quote, token, Attribute, Expr, Ident, LitChar, LitStr, Path,
+    PathArguments, PathSegment, Result, Token, Type,
 };
 
 use crate::utils::{to_kebab_case, LineIter};
@@ -20,7 +20,7 @@ pub struct Field {
     naming: Vec<Name>,
     env: Option<Box<Expr>>,
     consumer: Option<ConsumerAttr>,
-    external: Option<Ident>,
+    external: Option<Path>,
     name: Option<Ident>,
     postpr: Vec<PostprAttr>,
     help: Option<String>,
@@ -84,6 +84,11 @@ pub fn parse_ident(input: ParseStream) -> Result<Ident> {
     parse_arg(input)
 }
 
+#[inline(never)]
+pub fn parse_path(input: ParseStream) -> Result<Path> {
+    parse_arg(input)
+}
+
 #[inline(never)]
 pub fn parse_expr(input: ParseStream) -> Result<Expr> {
     parse_arg(input)
@@ -219,9 +224,9 @@ impl Field {
                         check_stage(&mut stage, 3, &keyword)?;
 
                         if input.peek(token::Paren) {
-                            res.external = Some(parse_ident(input)?);
-                        } else if res.name.is_some() {
-                            res.external = res.name.clone();
+                            res.external = Some(parse_path(input)?);
+                        } else if let Some(name) = &res.name {
+                            res.external = Some(ident_to_path(name.clone()));
                         } else {
                             break Err(
                                 input_copy.error("unnamed fields needs to have a name specified")
@@ -233,7 +238,7 @@ impl Field {
                     } else if keyword == "guard" {
                         check_stage(&mut stage, 4, &keyword)?;
                         let _ = parenthesized!(content in input);
-                        let guard_fn = content.parse::<Ident>()?;
+                        let guard_fn = content.parse::<Path>()?;
                         let _ = content.parse::<Token![,]>()?;
                         let msg = content.parse::<Expr>()?;
                         res.postpr
@@ -248,14 +253,13 @@ impl Field {
                             .push(PostprAttr::FallbackWith(span, Box::new(parse_expr(input)?)));
                     } else if keyword == "parse" {
                         check_stage(&mut stage, 4, &keyword)?;
-                        res.postpr
-                            .push(PostprAttr::Parse(span, parse_ident(input)?));
+                        res.postpr.push(PostprAttr::Parse(span, parse_path(input)?));
                     } else if keyword == "map" {
                         check_stage(&mut stage, 4, &keyword)?;
-                        res.postpr.push(PostprAttr::Map(span, parse_ident(input)?));
+                        res.postpr.push(PostprAttr::Map(span, parse_path(input)?));
                     } else if keyword == "complete" {
                         check_stage(&mut stage, 4, &keyword)?;
-                        let f = parse_ident(input)?;
+                        let f = parse_path(input)?;
                         res.postpr.push(PostprAttr::Complete(span, f));
                     } else if keyword == "many" {
                         check_stage(&mut stage, 4, &keyword)?;
@@ -468,3 +472,15 @@ impl ToTokens for Field {
         }
     }
 }
+
+fn ident_to_path(ident: Ident) -> Path {
+    Path {
+        leading_colon: None,
+        segments: vec![PathSegment {
+            ident,
+            arguments: PathArguments::None,
+        }]
+        .into_iter()
+        .collect(),
+    }
+}
diff --git a/src/_applicative.rs b/src/_applicative.rs
index 3cd92e3..7ec811b 100644
--- a/src/_applicative.rs
+++ b/src/_applicative.rs
@@ -69,7 +69,7 @@
 
 //! ## Applicative Functors
 //!
-//! `map` in `Functor` is limited to a single *value in a context*, `Applicativ Functor` extends it
+//! `map` in `Functor` is limited to a single *value in a context*, `Applicative Functor` extends it
 //! to operations combining multiple values, closest Rust analogy would be doing computations on
 //! `Option` or `Result` using only `?` and `Some`/`Ok` but without making any decisions that would
 //! change the shape of the output based on input values after they been extracted with `?`.
@@ -130,8 +130,28 @@
 //! datatypes: `struct` for product types and `enum` for sum types. To give an example -
 //! `cargo-show-asm` asks user to specify what to output - Intel or AT&T asm, LLVM or Rust's MIR
 //! and opts to represent it as one of four flags: `--intel`, `--att`, `--llvm` and `--mir`. While
-//! each flag can be though of a boolean value - present/absent - consuming it as an enum with four
-//! possible values is much more convenient compared to tuple with all the possible combinations.
+//! each flag can be though of a boolean value - present/absent - consuming it as an `enum` with four
+//! possible values is much more convenient compared to a struct-like thing that can have any
+//! combination of the flags inside:
+//!
+//! ```no_check
+//! /// Format selection as enum - program needs to deal with just one format
+//! enum Format {
+//!     Intel,
+//!     Att,
+//!     Llvm,
+//!     Mir
+//! }
+//!
+//! /// Format selection as enum - can represent any possible combination of formats
+//! struct Formats {
+//!     intel: bool,
+//!     att: bool,
+//!     llvm: bool,
+//!     mir: bool,
+//! }
+//! ```
+//!
 //! `Applicative` interface gives just enough power to compose simple parsers as an arbitrary tree
 //! ready for consumption.
 //!
diff --git a/src/args.rs b/src/args.rs
index e2a6686..e2cbc1d 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -187,6 +187,8 @@ mod inner {
         }
 
         #[cfg(feature = "autocomplete")]
+        /// Check if parser performs autocompletion
+        ///
         /// used by construct macro
         #[must_use]
         pub fn is_comp(&self) -> bool {
diff --git a/src/lib.rs b/src/lib.rs
index 593f1a6..8ed16b4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,8 +10,8 @@
 //! at once. Both APIs provide access to mostly the same features, some things are more convenient
 //! to do with derive (usually less typing), some - with combinatoric (usually maximum flexibility
 //! and reducing boilerplate structs). In most cases using just one would suffice. Whenever
-//! possible APIs share the same keywords and overall structure. Documentation for combinatoric API
-//! also explains how to perform the same action in derive style.
+//! possible APIs share the same keywords and overall structure. Documentation is shared and
+//! contains examples for both combinatoric and derive style.
 //!
 //! `bpaf` supports dynamic shell completion for `bash`, `zsh`, `fish` and `elvish`.
 
@@ -25,111 +25,145 @@
 //! - [Batteries included](crate::batteries)
 //! - [Q&A](https://github.com/pacak/bpaf/discussions/categories/q-a)
 
-//! # Quick start, derive edition
+//! # Quick start - combinatoric and derive APIs
+//!
+//! <details>
+//! <summary style="display: list-item;">Derive style API, click to expand</summary>
 //!
 //! 1. Add `bpaf` under `[dependencies]` in your `Cargo.toml`
-//! ```toml
-//! [dependencies]
-//! bpaf = { version = "0.6", features = ["derive"] }
-//! ```
+//!    ```toml
+//!    [dependencies]
+//!    bpaf = { version = "0.7", features = ["derive"] }
+//!    ```
 //!
 //! 2. Define a structure containing command line attributes and run generated function
-//! ```no_run
-//! use bpaf::Bpaf;
-//!
-//! #[derive(Clone, Debug, Bpaf)]
-//! #[bpaf(options, version)]
-//! /// Accept speed and distance, print them
-//! struct SpeedAndDistance {
-//!     /// Speed in KPH
-//!     speed: f64,
-//!     /// Distance in miles
-//!     distance: f64,
-//! }
+//!    ```no_run
+//!    use bpaf::Bpaf;
 //!
-//! fn main() {
-//!     // #[derive(Bpaf)] generates `speed_and_distance` function
-//!     let opts = speed_and_distance().run();
-//!     println!("Options: {:?}", opts);
-//! }
-//! ```
+//!    #[derive(Clone, Debug, Bpaf)]
+//!    #[bpaf(options, version)]
+//!    /// Accept speed and distance, print them
+//!    struct SpeedAndDistance {
+//!        /// Speed in KPH
+//!        speed: f64,
+//!        /// Distance in miles
+//!        distance: f64,
+//!    }
+//!
+//!    fn main() {
+//!        // #[derive(Bpaf)] generates `speed_and_distance` function
+//!        let opts = speed_and_distance().run();
+//!        println!("Options: {:?}", opts);
+//!    }
+//!    ```
 //!
 //! 3. Try to run the app
-//! ```console
-//! % very_basic --help
-//! Accept speed and distance, print them
+//!    ```console
+//!    % very_basic --help
+//!    Accept speed and distance, print them
 //!
-//! Usage: --speed ARG --distance ARG
+//!    Usage: --speed ARG --distance ARG
 //!
-//! Available options:
-//!         --speed <ARG>     Speed in KPH
-//!         --distance <ARG>  Distance in miles
-//!     -h, --help            Prints help information
-//!     -V, --version         Prints version information
+//!    Available options:
+//!            --speed <ARG>     Speed in KPH
+//!            --distance <ARG>  Distance in miles
+//!        -h, --help            Prints help information
+//!        -V, --version         Prints version information
 //!
-//! % very_basic --speed 100
-//! Expected --distance ARG, pass --help for usage information
+//!    % very_basic --speed 100
+//!    Expected --distance ARG, pass --help for usage information
 //!
-//! % very_basic --speed 100 --distance 500
-//! Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
+//!    % very_basic --speed 100 --distance 500
+//!    Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
 //!
-//! % very_basic --version
-//! Version: 0.5.0 (taken from Cargo.toml by default)
-//!```
-
-//! # Quick start, combinatoric edition
+//!    % very_basic --version
+//!    Version: 0.5.0 (taken from Cargo.toml by default)
+//!    ```
+//! 4. You can check the [derive tutorial](crate::_derive_tutorial) for more detailed information.
+//!
+//! </details>
+//!
+//! <details>
+//! <summary style="display: list-item;">Combinatoric style API, click to expand</summary>
 //!
 //! 1. Add `bpaf` under `[dependencies]` in your `Cargo.toml`
-//! ```toml
-//! [dependencies]
-//! bpaf = "0.6"
-//! ```
+//!    ```toml
+//!    [dependencies]
+//!    bpaf = "0.7"
+//!    ```
 //!
 //! 2. Declare parsers for components, combine them and run it
-//! ```no_run
-//! use bpaf::{construct, long, Parser};
-//! #[derive(Clone, Debug)]
-//! struct SpeedAndDistance {
-//!     /// Dpeed in KPH
-//!     speed: f64,
-//!     /// Distance in miles
-//!     distance: f64,
-//! }
+//!    ```no_run
+//!    use bpaf::{construct, long, Parser};
+//!    #[derive(Clone, Debug)]
+//!    struct SpeedAndDistance {
+//!        /// Dpeed in KPH
+//!        speed: f64,
+//!        /// Distance in miles
+//!        distance: f64,
+//!    }
 //!
-//! fn main() {
-//!     // primitive parsers
-//!     let speed = long("speed")
-//!         .help("Speed in KPG")
-//!         .argument::<f64>("SPEED");
+//!    fn main() {
+//!        // primitive parsers
+//!        let speed = long("speed")
+//!            .help("Speed in KPG")
+//!            .argument::<f64>("SPEED");
 //!
-//!     let distance = long("distance")
-//!         .help("Distance in miles")
-//!         .argument::<f64>("DIST");
+//!        let distance = long("distance")
+//!            .help("Distance in miles")
+//!            .argument::<f64>("DIST");
 //!
-//!     // parser containing information about both speed and distance
-//!     let parser = construct!(SpeedAndDistance { speed, distance });
+//!        // parser containing information about both speed and distance
+//!        let parser = construct!(SpeedAndDistance { speed, distance });
 //!
-//!     // option parser with metainformation attached
-//!     let speed_and_distance
-//!         = parser
-//!         .to_options()
-//!         .descr("Accept speed and distance, print them");
+//!        // option parser with metainformation attached
+//!        let speed_and_distance
+//!            = parser
+//!            .to_options()
+//!            .descr("Accept speed and distance, print them");
 //!
-//!     let opts = speed_and_distance.run();
-//!     println!("Options: {:?}", opts);
-//! }
-//! ```
+//!        let opts = speed_and_distance.run();
+//!        println!("Options: {:?}", opts);
+//!    }
+//!    ```
+//!
+//! 3. Try to run the app
+//!
+//!    ```console
+//!    % very_basic --help
+//!    Accept speed and distance, print them
+//!
+//!    Usage: --speed ARG --distance ARG
+//!
+//!    Available options:
+//!            --speed <ARG>     Speed in KPH
+//!            --distance <ARG>  Distance in miles
+//!        -h, --help            Prints help information
+//!        -V, --version         Prints version information
+//!
+//!    % very_basic --speed 100
+//!    Expected --distance ARG, pass --help for usage information
+//!
+//!    % very_basic --speed 100 --distance 500
+//!    Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
+//!
+//!    % very_basic --version
+//!    Version: 0.5.0 (taken from Cargo.toml by default)
+//!    ```
+//!
+//! 4. You can check the [combinatoric tutorial](crate::_combinatoric_tutorial) for more detailed information.
+//!
+//!
+//! </details>
 //!
-//! 3. Try to run it, output should be similar to derive version
-
 //! # Design goals: flexibility, reusability, correctness
-
+//!
 //! Library allows to consume command line arguments by building up parsers for individual
 //! arguments and combining those primitive parsers using mostly regular Rust code plus one macro.
 //! For example it's possible to take a parser that requires a single floating point number and
 //! transform it to a parser that takes several of them or takes it optionally so different
 //! subcommands or binaries can share a lot of the code:
-
+//!
 //! ```rust
 //! # use bpaf::*;
 //! // a regular function that doesn't depend on any context, you can export it
@@ -176,14 +210,16 @@
 //! ```
 //!
 //! Library follows **parse, don’t validate** approach to validation when possible. Usually you parse
-//! your values just once and get the results as a rust struct/enum with strict types rather than a
+//! your values just once and get the results as a Rust struct/enum with strict types rather than a
 //! stringly typed hashmap with stringly typed values in both combinatoric and derive APIs.
 
 //! # Design goals: restrictions
 //!
 //! The main restricting library sets is that you can't use parsed values (but not the fact that
 //! parser succeeded or failed) to decide how to parse subsequent values. In other words parsers
-//! don't have the monadic strength, only the applicative one.
+//! don't have the monadic strength, only the applicative one - for more detailed explanation see
+//! [Applicative functors? What is it all about](crate::_applicative).
+//!
 //!
 //! To give an example, you can implement this description:
 //!
@@ -216,7 +252,7 @@
 //!
 //! 1. Enable `autocomplete` feature:
 //!    ```toml
-//!    bpaf = { version = "0.6.0", features = ["autocomplete"] }
+//!    bpaf = { version = "0.7", features = ["autocomplete"] }
 //!    ```
 //! 2. Decorate [`argument`](NamedArg::argument) and [`positional`] parsers with
 //!    [`complete`](Parser::complete) to autocomplete argument values
@@ -261,7 +297,7 @@
 
 //! # More examples
 //!
-//! You can find a bunch more examples here: <https://github.com/pacak/bpaf/tree/master/examples>
+//! You can find a more examples here: <https://github.com/pacak/bpaf/tree/master/examples>
 //!
 //!
 //! They're usually documented or at least contain an explanation to important bits and you can see
@@ -320,6 +356,7 @@
 //!   bright-color = ["bpaf/bright-color"]
 //!   dull-color = ["bpaf/dull-color"]
 //!   ```
+//!   Disabled by default.
 
 #[macro_use]
 #[cfg(feature = "color")]
@@ -330,9 +367,11 @@ mod color;
 mod no_color;
 
 #[cfg(feature = "color")]
+#[doc(hidden)]
 pub use color::set_override;
 
 #[cfg(not(feature = "color"))]
+#[doc(hidden)]
 pub use no_color::set_override;
 
 #[cfg(feature = "extradocs")]
@@ -441,12 +480,18 @@ mod from_os_str;
 /// # }
 ///
 /// # { let a = short('a').switch(); let b = short('b').switch(); let c = short('c').switch();
-/// // parallel composition, tries all parsers, picks succeeding left most one:
+/// // parallel composition, tries all parsers, picks one that consumes the left most value,
+/// // or if they consume the same (or not at all) - the left most in a list
 /// construct!([a, b, c]);
 /// # }
 ///
 /// // defining primitive parsers inside construct macro :)
 /// construct!(a(short('a').switch()), b(long("arg").argument::<usize>("ARG")));
+///
+/// # { let a = short('a').switch();
+/// // defining a boxed parser
+/// construct!(a);
+/// # }
 /// ```
 ///
 /// # Combinatoric usage
diff --git a/src/params.rs b/src/params.rs
index 083dc3e..9db3608 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -926,6 +926,9 @@ pub struct ParseAny<T> {
 
 /// Take next unconsumed item on the command line as raw [`String`] or [`OsString`]
 ///
+/// **`any` is designed to consume items that don't fit into usual `flag`/`switch`/`positional`
+/// /`argument`/`command` classification**
+///
 /// `any` behaves similar to [`positional`] so you should be using it near the right most end of
 /// the consumer struct. Note, consuming "anything" also consumes `--help` unless restricted
 /// with `guard`. It's better stick to `positional` unless you are trying to consume raw options
diff --git a/src/structs.rs b/src/structs.rs
index 9f88f6f..731015b 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -142,7 +142,7 @@ where
 
 /// Parser that hides inner parser from usage line
 ///
-/// Otherwise the behavior is unchanged
+/// No other changes to the inner parser
 pub struct ParseHideUsage<P> {
     pub(crate) inner: P,
 }


Test patch:
diff --git a/bpaf_derive/src/field_tests.rs b/bpaf_derive/src/field_tests.rs
index 7dd1db1..b715946 100644
--- a/bpaf_derive/src/field_tests.rs
+++ b/bpaf_derive/src/field_tests.rs
@@ -120,6 +120,18 @@ fn derive_external_no_help() {
     assert_eq!(input.to_token_stream().to_string(), output.to_string());
 }
 
+#[test]
+fn derive_external_with_path() {
+    let input: NamedField = parse_quote! {
+        #[bpaf(external(path::level))]
+        number: f64
+    };
+    let output = quote! {
+        path::level()
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}
+
 #[test]
 fn derive_external_nohelp() {
     let input: NamedField = parse_quote! {
diff --git a/tests/pure_with.rs b/tests/pure_with.rs
index 90d587a..11e2528 100644
--- a/tests/pure_with.rs
+++ b/tests/pure_with.rs
@@ -2,7 +2,8 @@ use bpaf::*;
 
 #[test]
 fn default_value_using_pure_with_ok() {
-    // ~ this is a bit artifactial (we'd use fallback_with instead actually), but it demonstrates the concept
+    // ~ this is a bit artifactial (it's better to use fallback_with instead actually),
+    // but this example demonstrates the concept
     let a = short('t').argument::<u32>("N");
     let b = pure_with::<_, _, String>(|| Ok(42u32));
 
@@ -46,7 +47,7 @@ fn default_value_using_pure_with_ok_for_some() {
 fn default_value_using_pure_with_err_for_some() {
     let user_seeds = positional::<u32>("SEED").some("at least one required");
     let last_seeds = pure_with(|| {
-        // ~ trying to lookup the last used seeds but we're failing
+        // ~ trying to lookup the last used seeds but failing - parser fails, fallback works
         Err("oh, no!")
     });
     let default_seeds = pure(vec![1, 2]);