Repository: pacak/bpaf
Pull request #408 (2025-01-24 23:09:54)

Release 0.9.16
- treat `pure` as an implicit consumer - don't add unnecessary `.optional()` or `.many()`
- unbrainfart one of the examples


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 9c5363c..508794b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "bpaf"
-version = "0.9.15"
+version = "0.9.16"
 edition = "2021"
 categories = ["command-line-interface"]
 description = "A simple Command Line Argument Parser with parser combinators"
@@ -21,7 +21,7 @@ include = [
 
 
 [dependencies]
-bpaf_derive = { path = "./bpaf_derive", version = "=0.5.13", optional = true }
+bpaf_derive = { path = "./bpaf_derive", version = "=0.5.16", optional = true }
 owo-colors = { version = ">=3.5, <5.0", default-features = false, optional = true }
 supports-color = { version = ">=2.0.0, <4.0", optional = true }
 
diff --git a/Changelog.md b/Changelog.md
index a695a4d..4d18214 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,9 @@
 # Change Log
 
+## bpaf [0.9.16], 2025-01-24
+- treat `pure` as an implicit consumer - don't add unnecessary `.optional()` or `.many()`
+- unbrainfart one of the examples
+
 ## bpaf [0.9.15], 2024-10-08
 - a fix for a previous fix of fish completions, again - regenerate the files
 
diff --git a/bpaf_derive/Cargo.toml b/bpaf_derive/Cargo.toml
index 6e71dad..7530ed7 100644
--- a/bpaf_derive/Cargo.toml
+++ b/bpaf_derive/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "bpaf_derive"
-version = "0.5.13"
+version = "0.5.16"
 edition = "2018"
 categories = ["command-line-interface"]
 description = "Derive macros for bpaf Command Line Argument Parser"
diff --git a/bpaf_derive/src/named_field.rs b/bpaf_derive/src/named_field.rs
index 1fa6842..762335a 100644
--- a/bpaf_derive/src/named_field.rs
+++ b/bpaf_derive/src/named_field.rs
@@ -280,7 +280,7 @@ impl StructField {
         let span = ty.span();
 
         if !(postpr.iter().any(|p| matches!(p, Post::Parse(_)))
-            || matches!(cons, Consumer::External { .. }))
+            || matches!(cons, Consumer::External { .. } | Consumer::Pure { .. }))
         {
             match shape {
                 Shape::Optional(_) => postpr.insert(0, Post::Parse(PostParse::Optional { span })),
diff --git a/examples/env_logger.rs b/examples/env_logger.rs
index 058e364..1dbf06b 100644
--- a/examples/env_logger.rs
+++ b/examples/env_logger.rs
@@ -32,7 +32,7 @@ fn verbose() -> impl Parser<LevelFilter> {
         .count()
         .map(|l| {
             use LevelFilter::*;
-            [Off, Error, Warn, Info, Debug, Trace][l.max(5)]
+            [Off, Error, Warn, Info, Debug, Trace][l.clamp(0, 5)]
         })
 }
 


Test patch:
diff --git a/bpaf_derive/src/field_tests.rs b/bpaf_derive/src/field_tests.rs
index f3dc8ea..6de42fb 100644
--- a/bpaf_derive/src/field_tests.rs
+++ b/bpaf_derive/src/field_tests.rs
@@ -950,6 +950,42 @@ fn positional_bool() {
     assert_eq!(input.to_token_stream().to_string(), output.to_string());
 }
 
+#[test]
+fn pure_optional_named() {
+    let input: NamedField = parse_quote! {
+        #[bpaf(pure(x))]
+        flag: Option<Vec<X>>
+    };
+    let output = quote! {
+        ::bpaf::pure(x)
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}
+
+#[test]
+fn pure_vec_named() {
+    let input: NamedField = parse_quote! {
+        #[bpaf(pure(x))]
+        flag: Vec<X>
+    };
+    let output = quote! {
+        ::bpaf::pure(x)
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}
+
+#[test]
+fn pure_optional_pos() {
+    let input: UnnamedField = parse_quote! {
+        #[bpaf(pure(x))]
+         Option<Vec<X>>
+    };
+    let output = quote! {
+        ::bpaf::pure(x)
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}
+
 #[test]
 fn raw_literal() {
     let input: NamedField = parse_quote! {
diff --git a/tests/derive.rs b/tests/derive.rs
index b8a0c99..475ecfe 100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -123,3 +123,15 @@ Available commands:
     let r = parser.run_inner(&["one"]).unwrap();
     assert_eq!(r, One);
 }
+
+#[test]
+fn pure_optional() {
+    #[derive(Bpaf, Debug, Clone)]
+    #[bpaf(options)]
+    struct Opts {
+        #[bpaf(pure(Default::default()))]
+        foo: Option<Vec<u32>>,
+    }
+
+    assert_eq!(opts().run().foo, None);
+}