nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: mitsuhiko/minijinja
Pull request #452 (2024-03-29 18:10:26)

Support returning options from functions


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95e4bf4..738f029 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 All notable changes to MiniJinja are documented here.
 
+## 1.0.17
+
+- Added support for `Option<Into<Value>>` as return value from
+  functions.  #452
+
 ## 1.0.16
 
 - Tolerate underscores in number literals.  #443
diff --git a/minijinja/src/value/argtypes.rs b/minijinja/src/value/argtypes.rs
index 779a15d..aec46ce 100644
--- a/minijinja/src/value/argtypes.rs
+++ b/minijinja/src/value/argtypes.rs
@@ -982,6 +982,15 @@ impl From<usize> for Value {
     }
 }
 
+impl<I: Into<Value>> From<Option<I>> for Value {
+    fn from(value: Option<I>) -> Self {
+        match value {
+            Some(value) => value.into(),
+            None => Value::from(()),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;


Test patch:
diff --git a/minijinja/tests/test_templates.rs b/minijinja/tests/test_templates.rs
index 8b8ad04..6a48e66 100644
--- a/minijinja/tests/test_templates.rs
+++ b/minijinja/tests/test_templates.rs
@@ -8,8 +8,9 @@ use std::collections::BTreeMap;
 use std::fmt::Write;
 use std::{env, fs};
 
+use insta::assert_snapshot;
 use minijinja::value::{StructObject, Value};
-use minijinja::{context, Environment, Error, State};
+use minijinja::{context, render, Environment, Error, State};
 
 use similar_asserts::assert_eq;
 
@@ -522,3 +523,19 @@ fn test_render_to_write_state() {
     assert_eq!(state.lookup("foo"), Some(Value::from(42)));
     assert_eq!(state.call_macro("bar", &[]).ok().as_deref(), Some("x"));
 }
+
+#[test]
+fn test_functions() {
+    assert_snapshot!(
+        render!("{{ f() }}", f => Value::from_function(|| -> i32 { 42 })),
+        @"42"
+    );
+    assert_snapshot!(
+        render!("{{ f() }}", f => Value::from_function(|| -> Option<i32> { None })),
+        @"none"
+    );
+    assert_snapshot!(
+        render!("{{ f() }}", f => Value::from_function(|| -> Result<i32, Error> { Ok(23) })),
+        @"23"
+    );
+}
Repository: mitsuhiko/minijinja
Pull request #491 (2024-04-10 21:07:21)

Added support for sequence concatination with +
Added support for sequence concatenation with the plus operator for better compatibility with Jinja2.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2dae9a1..0138446 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@ All notable changes to MiniJinja are documented here.
 ## 1.0.20
 
 - Added support for implicit string concatenation for Jinja2 compatibility.  #489
+- Added support for sequence concatenation with the plus operator for Jinja2 compatibility.  #491
 
 ## 1.0.19
 
diff --git a/minijinja/src/value/ops.rs b/minijinja/src/value/ops.rs
index 3c39a0a..a446ae1 100644
--- a/minijinja/src/value/ops.rs
+++ b/minijinja/src/value/ops.rs
@@ -181,6 +181,11 @@ macro_rules! math_binop {
 }
 
 pub fn add(lhs: &Value, rhs: &Value) -> Result<Value, Error> {
+    if lhs.kind() == ValueKind::Seq && rhs.kind() == ValueKind::Seq {
+        return Ok(Value::from_iter(
+            lhs.try_iter_owned()?.chain(rhs.try_iter_owned()?),
+        ));
+    }
     match coerce(lhs, rhs) {
         Some(CoerceResult::I128(a, b)) => a
             .checked_add(b)


Test patch:
diff --git a/minijinja/tests/inputs/adding.txt b/minijinja/tests/inputs/adding.txt
index 7d3dc7b..5a8abed 100644
--- a/minijinja/tests/inputs/adding.txt
+++ b/minijinja/tests/inputs/adding.txt
@@ -6,11 +6,14 @@ Strings:
 {{ 'to' + name + 'you' }}!
 {{ 'to' + 'you' }}!
 
+Sequences:
+{{ [1] + [2] }}
+
 Adding:
 {{ 1 + 2 }}
 
 Minus:
-{{ 2 + 1 }}
+{{ 2 - 1 }}
 
 Divide:
 {{ 100 / 2 }}
\ No newline at end of file
diff --git a/minijinja/tests/snapshots/test_templates__vm@adding.txt.snap b/minijinja/tests/snapshots/test_templates__vm@adding.txt.snap
index 59897b1..b37751f 100644
--- a/minijinja/tests/snapshots/test_templates__vm@adding.txt.snap
+++ b/minijinja/tests/snapshots/test_templates__vm@adding.txt.snap
@@ -1,6 +1,6 @@
 ---
 source: minijinja/tests/test_templates.rs
-description: "Strings:\n{{ 'to' + name + 'you' }}!\n{{ 'to' + 'you' }}!\n\nAdding:\n{{ 1 + 2 }}\n\nMinus:\n{{ 2 + 1 }}\n\nDivide:\n{{ 100 / 2 }}"
+description: "Strings:\n{{ 'to' + name + 'you' }}!\n{{ 'to' + 'you' }}!\n\nSequences:\n{{ [1] + [2] }}\n\nAdding:\n{{ 1 + 2 }}\n\nMinus:\n{{ 2 - 1 }}\n\nDivide:\n{{ 100 / 2 }}"
 info:
   name: World
 input_file: minijinja/tests/inputs/adding.txt
@@ -9,12 +9,14 @@ Strings:
 toWorldyou!
 toyou!
 
+Sequences:
+[1, 2]
+
 Adding:
 3
 
 Minus:
-3
+1
 
 Divide:
 50.0
-
Repository: mitsuhiko/minijinja
Pull request #743 (2025-03-06 18:26:43)

Fix unknown_method_callback error reporting
This fixes a bug that an `unknown_method_callback` would not trigger the default error handling for unknown methods.  Now the error message is properly filled in if that callback returns `UnknownMethod`.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90430e1..3b60bda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ All notable changes to MiniJinja are documented here.
 - Do not panic if too large templates (too many lines or too many
   columns) are loaded.  The error reporting will be wrong in those
   cases but the templates will load.  #742
+- Fixed a bug that caused unknown method callbacks to not get
+  proper error reporting if they cannot find a method.  #743
 
 ## 2.8.0
 
diff --git a/minijinja/src/value/mod.rs b/minijinja/src/value/mod.rs
index 984223d..cf4c4bd 100644
--- a/minijinja/src/value/mod.rs
+++ b/minijinja/src/value/mod.rs
@@ -1590,8 +1590,21 @@ impl Value {
             Err(mut err) => {
                 if err.kind() == ErrorKind::UnknownMethod {
                     if let Some(ref callback) = state.env().unknown_method_callback {
-                        return callback(state, self, name, args);
-                    } else if err.detail().is_none() {
+                        match callback(state, self, name, args) {
+                            Ok(result) => return Ok(result),
+                            Err(callback_err) => {
+                                // if the callback fails with the same error, we
+                                // want to also attach the default detail if
+                                // it's missing
+                                if callback_err.kind() == ErrorKind::UnknownMethod {
+                                    err = callback_err;
+                                } else {
+                                    return Err(err);
+                                }
+                            }
+                        }
+                    }
+                    if err.detail().is_none() {
                         err.set_detail(format!("{} has no method named {}", self.kind(), name));
                     }
                 }


Test patch:
diff --git a/minijinja/tests/test_environment.rs b/minijinja/tests/test_environment.rs
index 24dda79..fe3dd08 100644
--- a/minijinja/tests/test_environment.rs
+++ b/minijinja/tests/test_environment.rs
@@ -173,6 +173,13 @@ fn test_unknown_method_callback() {
 
     let rv = env.render_str("{{ {'x': 42}.items() }}", ()).unwrap();
     assert_snapshot!(rv, @r###"[["x", 42]]"###);
+
+    let err = env.render_str("{{ [].does_not_exist() }}", ()).unwrap_err();
+    assert_eq!(err.kind(), ErrorKind::UnknownMethod);
+    assert_eq!(
+        err.detail(),
+        Some("sequence has no method named does_not_exist")
+    );
 }
 
 #[test]
Repository: pacak/bpaf
Pull request #87 (2022-09-30 15:10:05)

releasing...


Patch:
diff --git a/Changelog.md b/Changelog.md
index 96ff88d..07e3fe9 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,10 +1,11 @@
 # Change Log
 
-## bpaf [0.6.1] - unreleased
+## bpaf [0.6.1] - 2022-09-30
 - cosmetic improvements
 - completion info in sensors example
 - better errors in partially consumed optional items
 - better handling of -- during autcomplete
+- initial release of bpaf_cauwugo
 
 ## bpaf [0.6.0] - 2022-09-22
 # What's new in 0.6.0
diff --git a/bpaf_cauwugo/src/main.rs b/bpaf_cauwugo/src/main.rs
index d623c34..143a20f 100644
--- a/bpaf_cauwugo/src/main.rs
+++ b/bpaf_cauwugo/src/main.rs
@@ -4,6 +4,8 @@ use std::process::Command;
 fn main() -> std::io::Result<()> {
     if let Some(first) = std::env::args_os().nth(1) {
         let supported = first == "--help"
+            || first == "--version"
+            || first == "-V"
             || first == "add"
             || first == "build"
             || first == "check"
diff --git a/bpaf_cauwugo/src/opts.rs b/bpaf_cauwugo/src/opts.rs
index 2220e8b..beb1900 100644
--- a/bpaf_cauwugo/src/opts.rs
+++ b/bpaf_cauwugo/src/opts.rs
@@ -144,3 +144,8 @@ fn parse_exec_target(
             )
         })
 }
+
+#[test]
+fn options_are_sane() {
+    cauwugo_opts().check_invariants(true);
+}
diff --git a/src/info.rs b/src/info.rs
index 32d3537..6f47eb1 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -596,6 +596,7 @@ fn report_missing_items(err: Error) -> Error {
 
 fn perform_invariant_check(meta: &Meta, fresh: bool) {
     if fresh {
+        println!("Checking\n{:#?}", meta);
         to_usage_meta(meta);
     }
     match meta {


Test patch:
diff --git a/bpaf_cauwugo/src/test.rs b/bpaf_cauwugo/src/test.rs
index 3a2722a..95fd0cb 100644
--- a/bpaf_cauwugo/src/test.rs
+++ b/bpaf_cauwugo/src/test.rs
@@ -135,7 +135,7 @@ pub fn parse_specific() -> impl Parser<Test> {
 
 pub fn parse_test() -> impl Parser<Test> {
     let general = parse_test_general().map(Test::All);
-    construct!([parse_specific(), general])
+    construct!([general, parse_specific()])
         .to_options()
         .descr("Execute unit and integration tests")
         .command("test")
diff --git a/tests/completion.rs b/tests/completion.rs
index e1daf2e..83d9bd9 100644
--- a/tests/completion.rs
+++ b/tests/completion.rs
@@ -1052,3 +1052,12 @@ fn strict_positional_completion() {
         ],
     );
 }
+
+#[test]
+#[ignore]
+fn avoid_inserting_metavars() {
+    let parser = short('a').argument::<String>("A").to_options();
+
+    test_zsh_comp(&parser, &["-a", ""], &[["-a", "-a <A>", "", ""]]);
+    test_zsh_comp(&parser, &[""], &[["-a", "-a <A>", "", ""]]);
+}
Repository: pacak/bpaf
Pull request #177 (2023-03-03 22:17:06)

some extra examples/tests


Patch:
diff --git a/examples/many_comma_separated_args.rs b/examples/many_comma_separated_args.rs
new file mode 100644
index 0000000..858b328
--- /dev/null
+++ b/examples/many_comma_separated_args.rs
@@ -0,0 +1,20 @@
+use bpaf::*;
+use std::str::FromStr;
+
+// --ports 1,2,3 --ports 4,5   => [1,2,3,4,5]
+fn args() -> impl Parser<Vec<u16>> {
+    long("ports")
+        .help("Comma separated list of ports")
+        .argument::<String>("PORTS")
+        .parse(|s| {
+            s.split(",")
+                .map(|c| u16::from_str(c))
+                .collect::<Result<Vec<_>, _>>()
+        })
+        .many()
+        .map(|nested| nested.into_iter().flatten().collect())
+}
+
+fn main() {
+    println!("{:?}", args().to_options().run());
+}
diff --git a/examples/many_comma_separated_args_derive.rs b/examples/many_comma_separated_args_derive.rs
new file mode 100644
index 0000000..5886227
--- /dev/null
+++ b/examples/many_comma_separated_args_derive.rs
@@ -0,0 +1,30 @@
+use bpaf::*;
+use std::{num::ParseIntError, str::FromStr};
+
+fn split_and_parse(s: String) -> Result<Vec<u16>, ParseIntError> {
+    s.split(",")
+        .map(|c| u16::from_str(c))
+        .collect::<Result<Vec<_>, _>>()
+}
+
+fn flatten_vec(vv: Vec<Vec<u16>>) -> Vec<u16> {
+    vv.into_iter().flatten().collect()
+}
+
+#[derive(Debug, Clone, Bpaf)]
+#[allow(dead_code)]
+struct Opts {
+    #[bpaf(
+        long,
+        argument::<String>("PORTS"),
+        parse(split_and_parse),
+        many,
+        map(flatten_vec)
+    )]
+    /// Comma separated list of ports
+    ports: Vec<u16>,
+}
+
+fn main() {
+    println!("{:?}", opts().to_options().run());
+}
diff --git a/examples/very_custom_usage.rs b/examples/very_custom_usage.rs
index 695004c..1f50eda 100644
--- a/examples/very_custom_usage.rs
+++ b/examples/very_custom_usage.rs
@@ -11,6 +11,7 @@ use bpaf::*;
 // At this point cmd_usage can be any parser that produces Cmd
 #[derive(Debug, Clone, Bpaf)]
 #[bpaf(options)]
+#[allow(dead_code)]
 enum Opts {
     Cmd(#[bpaf(external(cmd_usage))] Cmd),
 }


Test patch:
diff --git a/tests/help_format.rs b/tests/help_format.rs
index 76d653f..030a4cb 100644
--- a/tests/help_format.rs
+++ b/tests/help_format.rs
@@ -157,3 +157,41 @@ Available options:
 
     assert_eq!(r, expected);
 }
+
+#[test]
+fn enum_with_docs() {
+    #[derive(Debug, Clone, Bpaf)]
+    /// present
+    ///
+    /// Absent
+    enum Mode {
+        /// help
+        ///
+        /// absent
+        Intel,
+
+        /// help
+        ///
+        /// Hidden
+        Att,
+    }
+
+    let r = mode()
+        .group_help("group help")
+        .to_options()
+        .run_inner(Args::from(&["--help"]))
+        .unwrap_err()
+        .unwrap_stdout();
+
+    let expected = "\
+Usage: (--intel | --att)
+
+Available options:
+  group help
+        --intel  help
+        --att    help
+
+    -h, --help   Prints help information
+";
+    assert_eq!(r, expected);
+}
Repository: pacak/bpaf
Pull request #188 (2023-03-24 21:15:06)

bpaf_derive: derive command for singleton structs
Fixes https://github.com/pacak/bpaf/issues/163


Patch:
diff --git a/bpaf_derive/src/top.rs b/bpaf_derive/src/top.rs
index ac076d2..9b7b442 100644
--- a/bpaf_derive/src/top.rs
+++ b/bpaf_derive/src/top.rs
@@ -113,6 +113,9 @@ impl Parse for Fields {
             let _ = parenthesized!(content in input);
             let fields = content.parse_terminated(Field::parse_unnamed, token::Comma)?;
             Ok(Fields::Unnamed(fields))
+        } else if input.peek(token::Semi) {
+            let _ = input.parse::<token::Semi>();
+            Ok(Fields::NoFields)
         } else {
             Err(input.error("Expected named or unnamed struct"))
         }


Test patch:
diff --git a/bpaf_derive/src/top_tests.rs b/bpaf_derive/src/top_tests.rs
index df2c1b1..49d1f0d 100644
--- a/bpaf_derive/src/top_tests.rs
+++ b/bpaf_derive/src/top_tests.rs
@@ -793,3 +793,24 @@ fn no_fields_declaration() {
 
     assert_eq!(top.to_token_stream().to_string(), expected.to_string());
 }
+
+#[test]
+fn single_unit_command() {
+    let top: Top = parse_quote! {
+        #[bpaf(command)]
+        struct One;
+    };
+
+    let expected = quote! {
+        fn one() -> impl ::bpaf::Parser<One> {
+            #[allow(unused_imports)]
+            use ::bpaf::Parser;
+            {
+                let inner_cmd = ::bpaf::pure(One).to_options();
+                ::bpaf::command("one", inner_cmd)
+            }
+        }
+    };
+
+    assert_eq!(top.to_token_stream().to_string(), expected.to_string());
+}
diff --git a/tests/derive.rs b/tests/derive.rs
index ca3eea7..eaccb21 100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -100,3 +100,29 @@ Available commands:
 ";
     assert_eq!(expected_help, help);
 }
+
+#[test]
+fn single_unit_command() {
+    #[derive(Bpaf, Debug, Clone, Eq, PartialEq)]
+    #[bpaf(command)]
+    struct One;
+
+    let parser = one().to_options();
+    let help = parser
+        .run_inner(bpaf::Args::from(&["--help"]))
+        .unwrap_err()
+        .unwrap_stdout();
+    let expected = "\
+Usage: COMMAND ...
+
+Available options:
+    -h, --help  Prints help information
+
+Available commands:
+    one
+";
+    assert_eq!(help, expected);
+
+    let r = parser.run_inner(bpaf::Args::from(&["one"])).unwrap();
+    assert_eq!(r, One);
+}
Repository: pacak/bpaf
Pull request #189 (2023-03-24 23:31:24)

Hidden env
Fixes https://github.com/pacak/bpaf/issues/173


Patch:
diff --git a/src/params.rs b/src/params.rs
index 35952e0..d707440 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -196,7 +196,10 @@ pub fn long(long: &'static str) -> NamedArg {
 /// $ NO_COLOR=1 app --do-something
 /// ```
 ///
-/// **For combinatoric usage you must specify either short or long key if you start the chain from `env`.**
+/// If you don't specify a short or a long name - whole argument is going to be absent from the
+/// help message. Use it combined with a named or positional argument to have a hidden fallback
+/// that wouldn't leak sensitive info.
+
 ///
 #[doc = include_str!("docs/short_long_env.md")]
 #[must_use]
@@ -647,13 +650,6 @@ impl<T: Clone + 'static> Parser<T> for ParseFlag<T> {
 }
 
 fn build_argument<T>(named: NamedArg, metavar: &'static str) -> ParseArgument<T> {
-    if !named.env.is_empty() {
-        // mostly cosmetic reasons
-        assert!(
-            !(named.short.is_empty() && named.long.is_empty()),
-            "env fallback can only be used if name is present"
-        );
-    }
     ParseArgument {
         named,
         metavar,
@@ -739,7 +735,11 @@ where
     }
 
     fn meta(&self) -> Meta {
-        Meta::Item(self.item())
+        if self.named.short.is_empty() && self.named.long.is_empty() {
+            Meta::Skip
+        } else {
+            Meta::Item(self.item())
+        }
     }
 }
 


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index c34e50d..6078f89 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -859,6 +859,31 @@ fn long_path_in_construct() {
     let _ = construct!(::std::option::Option::Some(b));
 }
 
+#[test]
+fn hidden_env() {
+    let name = "BPAF_SECRET_API_KEY";
+    let visible = long("key")
+        .help("use this secret key\ntwo lines")
+        .argument::<String>("KEY");
+    let hidden = env(name).argument("KEY");
+    let parser = construct!([visible, hidden]).to_options();
+
+    let help = parser
+        .run_inner(Args::from(&["-h"]))
+        .unwrap_err()
+        .unwrap_stdout();
+
+    let expected = "\
+Usage: --key KEY
+
+Available options:
+        --key <KEY>  use this secret key two lines
+    -h, --help       Prints help information
+";
+
+    assert_eq!(help, expected);
+}
+
 #[test]
 fn env_variable() {
     set_override(false);
Repository: pacak/bpaf
Pull request #191 (2023-03-25 16:27:05)

anywhere can consume nothing
fixes https://github.com/pacak/bpaf/issues/153


Patch:
diff --git a/src/structs.rs b/src/structs.rs
index a1870fe..db7885b 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -922,6 +922,11 @@ where
 
         #[cfg(feature = "autocomplete")]
         args.swap_comps_with(&mut comp_items);
+
+        if let Ok(val) = self.inner.eval(&mut Args::from(&[])) {
+            return Ok(val);
+        }
+
         Err(Error::Missing(best_missing))
     }
 


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 09b6310..53696a0 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -1936,3 +1936,10 @@ fn default_for_some() {
 
     assert_eq!(r, vec![1, 2, 3]);
 }
+
+#[test]
+fn anywhere_can_consume_nothing() {
+    let parser = positional::<String>("args").many().anywhere().to_options();
+    let r = parser.run_inner(Args::from(&[])).unwrap();
+    assert!(r.is_empty());
+}
Repository: pacak/bpaf
Pull request #256 (2023-08-01 14:01:30)

fix: emit a level 2 heading for sub commands in markdown
Currently, the markdown generated emits `# Header`, level one.

When the markdown is rendered to HTML, it creates `<h1>` tags, which are incorrect from the SEO point of view. Only one `h1` should be present in a page.

PR emits a `h1`  only for the app name and `h2` for the rest.




Patch:
diff --git a/src/buffer/html.rs b/src/buffer/html.rs
index f426eee..dd03e88 100644
--- a/src/buffer/html.rs
+++ b/src/buffer/html.rs
@@ -319,6 +319,7 @@ impl Doc {
         let mut mono = 0;
         let mut def_list = false;
         let mut code_block = false;
+        let mut app_name_seen = false;
         for (ix, token) in self.tokens.iter().copied().enumerate() {
             match token {
                 Token::Text { bytes, style } => {
@@ -384,7 +385,12 @@ impl Doc {
                     match b {
                         Block::Header => {
                             blank_markdown_line(&mut res);
-                            res.push_str("# ");
+                            if app_name_seen {
+                                res.push_str("## ");
+                            } else {
+                                res.push_str("# ");
+                                app_name_seen = true
+                            }
                         }
                         Block::Section2 => {
                             res.push_str("");


Test patch:
diff --git a/tests/markdown.rs b/tests/markdown.rs
index ec5dcf1..4a08bd4 100644
--- a/tests/markdown.rs
+++ b/tests/markdown.rs
@@ -75,7 +75,7 @@ fn nested() {
   * [`options alpha`↴](#options-alpha)
   * [`options beta`↴](#options-beta)
 
-# options
+## options
 
 Options
 
@@ -91,7 +91,7 @@ Options
 - **`beta`** &mdash; \n  Beta
 
 
-# options alpha
+## options alpha
 
 Alpha
 
@@ -100,7 +100,7 @@ Alpha
 - **`-h`**, **`--help`** &mdash; \n  Prints help information
 
 
-# options beta
+## options beta
 
 Beta
Repository: pacak/bpaf
Pull request #348 (2024-03-19 00:09:07)

Added anywhere support for derive
I got curious, and wanted to add support for `anywhere` for derive after https://github.com/pacak/bpaf/discussions/347.

Overall I just grepped for keywords to know where I needed to add it. So feel free to nit, if you want any changes.

Additionally, I tested that it works locally, but I didn't add any tests to assert it. I'll happily add some tests, just mention where you prefer I add them.







Patch:
diff --git a/bpaf_derive/src/attrs.rs b/bpaf_derive/src/attrs.rs
index 4f665ac..0627e0d 100644
--- a/bpaf_derive/src/attrs.rs
+++ b/bpaf_derive/src/attrs.rs
@@ -217,6 +217,7 @@ impl ToTokens for PostParse {
             PostParse::Optional { .. } => quote!(optional()),
             PostParse::Parse { f, .. } => quote!(parse(#f)),
             PostParse::Strict { .. } => quote!(strict()),
+            PostParse::Anywhere { .. } => quote!(anywhere()),
         }
         .to_tokens(tokens);
     }
@@ -255,6 +256,7 @@ pub(crate) enum PostParse {
     Optional { span: Span },
     Parse { span: Span, f: Box<Expr> },
     Strict { span: Span },
+    Anywhere { span: Span },
 }
 impl PostParse {
     fn span(&self) -> Span {
@@ -268,7 +270,8 @@ impl PostParse {
             | Self::Map { span, .. }
             | Self::Optional { span }
             | Self::Parse { span, .. }
-            | Self::Strict { span } => *span,
+            | Self::Strict { span }
+            | Self::Anywhere { span } => *span,
         }
     }
 }
@@ -480,6 +483,8 @@ impl PostParse {
         } else if kw == "some" {
             let msg = parse_arg(input)?;
             Self::Some_ { span, msg }
+        } else if kw == "anywhere" {
+            Self::Anywhere { span }
         } else {
             return Ok(None);
         }))


Test patch:
diff --git a/bpaf_derive/src/field_tests.rs b/bpaf_derive/src/field_tests.rs
index 45a37f5..1bc3e0e 100644
--- a/bpaf_derive/src/field_tests.rs
+++ b/bpaf_derive/src/field_tests.rs
@@ -947,3 +947,15 @@ fn raw_literal() {
     };
     assert_eq!(input.to_token_stream().to_string(), output.to_string());
 }
+
+#[test]
+fn any_anywhere() {
+    let input: NamedField = parse_quote! {
+        #[bpaf(any::<isize>("LIMIT", isize_to_usize), anywhere)]
+        num: isize
+    };
+    let output = quote! {
+        ::bpaf::any::<isize, _, _>("LIMIT", isize_to_usize).anywhere()
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}