Repository: pacak/bpaf
Pull request #226 (2023-07-04 12:09:17)

take expressions and allow trailing commas in more places in bpaf_derive
plus tests

Fixes https://github.com/pacak/bpaf/issues/223



Patch:
diff --git a/bpaf_derive/src/attrs.rs b/bpaf_derive/src/attrs.rs
index 7e1854e..df9d724 100644
--- a/bpaf_derive/src/attrs.rs
+++ b/bpaf_derive/src/attrs.rs
@@ -9,7 +9,7 @@ use crate::{
     help::Help,
     utils::{
         doc_comment, parse_arg, parse_arg2, parse_expr, parse_lit_char, parse_lit_str,
-        parse_opt_metavar, parse_path, to_kebab_case,
+        parse_opt_metavar, to_kebab_case,
     },
 };
 
@@ -248,10 +248,10 @@ pub(crate) enum PostParse {
     Catch { span: Span },
     Many { span: Span },
     Count { span: Span },
-    Some_ { span: Span, msg: LitStr },
-    Map { span: Span, f: Path },
+    Some_ { span: Span, msg: Box<Expr> },
+    Map { span: Span, f: Box<Expr> },
     Optional { span: Span },
-    Parse { span: Span, f: Path },
+    Parse { span: Span, f: Box<Expr> },
     Strict { span: Span },
 }
 impl PostParse {
@@ -274,7 +274,7 @@ impl PostParse {
 pub(crate) enum PostDecor {
     Complete {
         span: Span,
-        f: Path,
+        f: Box<Expr>,
     },
     CompleteGroup {
         span: Span,
@@ -296,7 +296,7 @@ pub(crate) enum PostDecor {
     },
     FallbackWith {
         span: Span,
-        f: Box<Path>,
+        f: Box<Expr>,
     },
     Last {
         span: Span,
@@ -307,7 +307,7 @@ pub(crate) enum PostDecor {
     },
     Guard {
         span: Span,
-        check: Path,
+        check: Box<Expr>,
         msg: Box<Expr>,
     },
     Hide {
@@ -461,17 +461,17 @@ impl PostParse {
         } else if kw == "count" {
             Self::Count { span }
         } else if kw == "map" {
-            let f = parse_path(input)?;
+            let f = parse_arg(input)?;
             Self::Map { span, f }
         } else if kw == "optional" {
             Self::Optional { span }
         } else if kw == "parse" {
-            let f = parse_path(input)?;
+            let f = parse_arg(input)?;
             Self::Parse { span, f }
         } else if kw == "strict" {
             Self::Strict { span }
         } else if kw == "some" {
-            let msg = parse_lit_str(input)?;
+            let msg = parse_arg(input)?;
             Self::Some_ { span, msg }
         } else {
             return Ok(None);
@@ -483,7 +483,7 @@ impl PostDecor {
     pub(crate) fn parse(input: ParseStream, kw: &Ident) -> Result<Option<Self>> {
         let span = kw.span();
         Ok(Some(if kw == "complete" {
-            let f = parse_path(input)?;
+            let f = parse_arg(input)?;
             Self::Complete { span, f }
         } else if kw == "group" {
             let group = parse_lit_str(input)?;
@@ -501,7 +501,7 @@ impl PostDecor {
         } else if kw == "last" {
             Self::Last { span }
         } else if kw == "fallback_with" {
-            let f = Box::new(parse_path(input)?);
+            let f = parse_expr(input)?;
             Self::FallbackWith { span, f }
         } else if kw == "group_help" {
             let doc = parse_expr(input)?;
@@ -577,6 +577,9 @@ impl Parse for FieldAttrs {
                 break;
             }
             input.parse::<token::Comma>()?;
+            if input.is_empty() {
+                break;
+            }
         }
         res.validate()?;
         Ok(res)
diff --git a/bpaf_derive/src/td.rs b/bpaf_derive/src/td.rs
index 58c096b..a97febc 100644
--- a/bpaf_derive/src/td.rs
+++ b/bpaf_derive/src/td.rs
@@ -210,6 +210,9 @@ impl Parse for TopInfo {
                 break;
             }
             input.parse::<token::Comma>()?;
+            if input.is_empty() {
+                break;
+            }
         }
 
         Ok(TopInfo {
@@ -289,6 +292,9 @@ impl Parse for Ed {
                 break;
             }
             input.parse::<token::Comma>()?;
+            if input.is_empty() {
+                break;
+            }
         }
 
         Ok(Ed { skip, attrs })
diff --git a/bpaf_derive/src/utils.rs b/bpaf_derive/src/utils.rs
index 0e04bb2..5dcc4c4 100644
--- a/bpaf_derive/src/utils.rs
+++ b/bpaf_derive/src/utils.rs
@@ -1,7 +1,7 @@
 use syn::{
     parenthesized,
     parse::{Parse, ParseStream},
-    token, Attribute, Expr, LitChar, LitStr, Path, Result,
+    token, Attribute, Expr, LitChar, LitStr, Result,
 };
 
 pub(crate) fn parse_arg<T: Parse>(input: ParseStream) -> Result<T> {
@@ -39,11 +39,6 @@ pub(crate) fn parse_lit_str(input: ParseStream) -> Result<LitStr> {
     parse_arg(input)
 }
 
-#[inline(never)]
-pub(crate) fn parse_path(input: ParseStream) -> Result<Path> {
-    parse_arg(input)
-}
-
 #[inline(never)]
 pub(crate) fn parse_expr(input: ParseStream) -> Result<Box<Expr>> {
     Ok(Box::new(parse_arg(input)?))
diff --git a/src/buffer/html.rs b/src/buffer/html.rs
index fe1aaab..712edcd 100644
--- a/src/buffer/html.rs
+++ b/src/buffer/html.rs
@@ -57,7 +57,7 @@ fn collect_html(app: String, meta: &Meta, info: &Info) -> Doc {
 impl<T> OptionParser<T> {
     /// Render command line documentation for the app into html/markdown mix
     pub fn render_html(&self, app: impl Into<String>) -> String {
-        collect_html(app.into(), &self.inner.meta(), &self.info).render_html(true, true)
+        collect_html(app.into(), &self.inner.meta(), &self.info).render_html(true, false)
     }
 }
 


Test patch:
diff --git a/bpaf_derive/src/field_tests.rs b/bpaf_derive/src/field_tests.rs
index ea15cdb..2d1e8d0 100644
--- a/bpaf_derive/src/field_tests.rs
+++ b/bpaf_derive/src/field_tests.rs
@@ -844,7 +844,7 @@ fn argument_with_manual_parse() {
 #[test]
 fn optional_external_strange() {
     let input: NamedField = parse_quote! {
-        #[bpaf(optional, external(seed))]
+        #[bpaf(optional, external(seed),)]
         number: u32
     };
 
@@ -854,6 +854,26 @@ fn optional_external_strange() {
     assert_eq!(input.to_token_stream().to_string(), output.to_string());
 }
 
+#[test]
+fn fallback_with_lambda() {
+    let input: NamedField = parse_quote! {
+        /// help
+        #[bpaf(
+            argument::<String>("FLAGS"),
+            fallback_with(|| Ok::<_, ()>("http-only")),
+        )]
+        session_flags: String
+    };
+
+    let output = quote! {
+        ::bpaf::long("session-flags")
+            .help("help")
+            .argument::<String>("FLAGS")
+            .fallback_with(| | Ok::<_,()>("http-only"))
+    };
+    assert_eq!(input.to_token_stream().to_string(), output.to_string());
+}
+
 #[test]
 fn positional_bool() {
     let input: NamedField = parse_quote! {
diff --git a/bpaf_derive/src/top_tests.rs b/bpaf_derive/src/top_tests.rs
index 77201d1..53e2be9 100644
--- a/bpaf_derive/src/top_tests.rs
+++ b/bpaf_derive/src/top_tests.rs
@@ -680,8 +680,8 @@ fn fallback_for_options() {
 #[test]
 fn implicitly_named_switch() {
     let top: Top = parse_quote! {
-        #[bpaf(options, fallback(Opts::Dummy))]
-        struct Opts (#[bpaf(long("release"), switch)] bool);
+        #[bpaf(options, fallback(Opts::Dummy),)]
+        struct Opts (#[bpaf(long("release"), switch,)] bool);
     };
 
     let expected = quote! {
@@ -703,12 +703,12 @@ fn implicitly_named_switch() {
 #[test]
 fn fallback_for_enum() {
     let top: Top = parse_quote! {
-        #[bpaf(fallback(Decision::No))]
+        #[bpaf(fallback(Decision::No),)]
         enum Decision {
             Yes,
-            #[bpaf(short, long("nay"))]
+            #[bpaf(short, long("nay"),)]
             No,
-            #[bpaf(skip)]
+            #[bpaf(skip,)]
             Undecided,
         }
     };
@@ -843,7 +843,7 @@ fn single_unit_command() {
 #[test]
 fn single_unit_adjacent_command() {
     let top: Top = parse_quote! {
-        #[bpaf(command, adjacent)]
+        #[bpaf(command, adjacent,)]
         struct One;
     };