Repository: pacak/bpaf
Pull request #190 (2023-03-25 12:57:09)

Reduce binary size


Patch:
diff --git a/src/info.rs b/src/info.rs
index 3b364df..2ce6012 100644
--- a/src/info.rs
+++ b/src/info.rs
@@ -592,7 +592,7 @@ impl From<Error> for ParseFailure {
             Error::ParseFailure(pf) => pf,
             Error::Missing(items) => ParseFailure::Stderr(format!(
                 "Expected {}, pass --help for usage information",
-                Meta::Or(items.into_iter().map(Meta::Item).collect::<Vec<_>>())
+                Meta::Or(items.into_iter().map(Meta::from).collect::<Vec<_>>())
             )),
         }
     }
@@ -616,8 +616,8 @@ fn perform_invariant_check(meta: &Meta, fresh: bool) {
         Meta::HideUsage(x) | Meta::Optional(x) | Meta::Many(x) | Meta::Decorated(x, _, _) => {
             perform_invariant_check(x, false);
         }
-        Meta::Item(i) => match i {
-            Item::Command { meta, .. } => perform_invariant_check(meta, true),
+        Meta::Item(i) => match &**i {
+            Item::Command { meta, .. } => perform_invariant_check(&meta, true),
             Item::Positional { .. }
             | Item::Flag { .. }
             | Item::Argument { .. }
diff --git a/src/item.rs b/src/item.rs
index 9beadfa..113d325 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -57,10 +57,11 @@ impl From<&NamedArg> for ShortLong {
 impl Item {
     #[must_use]
     pub(crate) fn required(self, required: bool) -> Meta {
+        let boxed = Meta::from(self);
         if required {
-            Meta::Item(self)
+            boxed
         } else {
-            Meta::Optional(Box::new(Meta::Item(self)))
+            Meta::Optional(Box::new(boxed))
         }
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index ad5d6c3..ab8bec9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1129,14 +1129,14 @@ pub trait Parser<T> {
         since = "0.5.0",
         note = "instead of a.or_else(b) you should use construct!([a, b])"
     )]
-    fn or_else<P>(self, alt: P) -> ParseOrElse<Self, P>
+    fn or_else<P>(self, alt: P) -> ParseOrElse<T>
     where
-        Self: Sized + Parser<T>,
-        P: Sized + Parser<T>,
+        Self: Sized + Parser<T> + 'static,
+        P: Sized + Parser<T> + 'static,
     {
         ParseOrElse {
-            this: self,
-            that: alt,
+            this: Box::new(self),
+            that: Box::new(alt),
         }
     }
     // }}}
diff --git a/src/meta.rs b/src/meta.rs
index d9244e3..ac9a42d 100644
--- a/src/meta.rs
+++ b/src/meta.rs
@@ -12,7 +12,7 @@ pub enum Meta {
     And(Vec<Meta>),
     Or(Vec<Meta>),
     Optional(Box<Meta>),
-    Item(Item),
+    Item(Box<Item>),
     Many(Box<Meta>),
     Decorated(Box<Meta>, String, DecorPlace),
     Skip,
@@ -28,6 +28,12 @@ impl std::fmt::Display for Meta {
     }
 }
 
+impl From<Item> for Meta {
+    fn from(value: Item) -> Self {
+        Meta::Item(Box::new(value))
+    }
+}
+
 impl Meta {
     fn alts(self, to: &mut Vec<Meta>) {
         match self {
@@ -56,7 +62,7 @@ impl Meta {
                     x.collect_shorts(flags, args);
                 }
             }
-            Meta::Item(m) => match m {
+            Meta::Item(m) => match &**m {
                 Item::Positional { .. } | Item::Command { .. } => {}
                 Item::Flag { shorts, .. } => flags.extend(shorts),
                 Item::MultiArg { shorts, .. } | Item::Argument { shorts, .. } => {
diff --git a/src/meta_usage.rs b/src/meta_usage.rs
index b462916..f3f16c7 100644
--- a/src/meta_usage.rs
+++ b/src/meta_usage.rs
@@ -125,7 +125,7 @@ fn collect_usage_meta(meta: &Meta, is_pos: &mut bool) -> Option<UsageMeta> {
         }
         Meta::Decorated(meta, _, _) => collect_usage_meta(meta, is_pos)?,
         Meta::HideUsage(_) | Meta::Skip => return None,
-        Meta::Item(i) => match i {
+        Meta::Item(i) => match &**i {
             Item::Positional {
                 metavar, strict, ..
             } => {
diff --git a/src/params.rs b/src/params.rs
index d707440..bcbdbf4 100644
--- a/src/params.rs
+++ b/src/params.rs
@@ -591,7 +591,7 @@ impl<T> Parser<T> for ParseCommand<T> {
     }
 
     fn meta(&self) -> Meta {
-        Meta::Item(self.item())
+        Meta::from(self.item())
     }
 }
 
@@ -694,6 +694,12 @@ impl<T> ParseArgument<T> {
     }
 
     fn take_argument(&self, args: &mut Args) -> Result<OsString, Error> {
+        if self.named.short.is_empty() && self.named.long.is_empty() {
+            if let Some(name) = self.named.env.first() {
+                let msg = format!("env variable {} is not set", name);
+                return Err(Error::Message(msg, true));
+            }
+        }
         match args.take_arg(&self.named, self.adjacent) {
             Ok(Some(w)) => {
                 #[cfg(feature = "autocomplete")]
@@ -738,7 +744,7 @@ where
         if self.named.short.is_empty() && self.named.long.is_empty() {
             Meta::Skip
         } else {
-            Meta::Item(self.item())
+            Meta::from(self.item())
         }
     }
 }
@@ -854,12 +860,10 @@ impl<T> ParsePositional<T> {
     }
 
     fn meta(&self) -> Meta {
-        Meta::Item({
-            Item::Positional {
-                metavar: self.metavar,
-                help: self.help.clone(),
-                strict: self.strict,
-            }
+        Meta::from(Item::Positional {
+            metavar: self.metavar,
+            help: self.help.clone(),
+            strict: self.strict,
         })
     }
 }
@@ -972,7 +976,7 @@ impl<T> ParseAny<T> {
     }
 
     fn meta(&self) -> Meta {
-        Meta::Item(self.item())
+        Meta::from(self.item())
     }
 
     fn item(&self) -> Item {
diff --git a/src/structs.rs b/src/structs.rs
index 03efc0f..a1870fe 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -168,16 +168,12 @@ where
 
 /// Parser that tries to either of two parsers and uses one that succeeeds, created with
 /// [`Parser::or_else`].
-pub struct ParseOrElse<A, B> {
-    pub(crate) this: A,
-    pub(crate) that: B,
+pub struct ParseOrElse<T> {
+    pub(crate) this: Box<dyn Parser<T>>,
+    pub(crate) that: Box<dyn Parser<T>>,
 }
 
-impl<A, B, T> Parser<T> for ParseOrElse<A, B>
-where
-    A: Parser<T>,
-    B: Parser<T>,
-{
+impl<T> Parser<T> for ParseOrElse<T> {
     fn eval(&self, args: &mut Args) -> Result<T, Error> {
         #[cfg(feature = "autocomplete")]
         let mut comp_items = Vec::new();
@@ -854,7 +850,7 @@ where
                     }
                     res
                 }
-                Meta::Item(i) => vec![i.clone()],
+                Meta::Item(i) => vec![*i.clone()],
                 Meta::Optional(m) | Meta::Many(m) | Meta::Decorated(m, _, _) => meta_items(&*m),
                 Meta::Skip | Meta::HideUsage(_) => Vec::new(),
             }
@@ -944,20 +940,27 @@ fn classify_anywhere(meta: Meta) -> Meta {
         let mut iter = xs.iter();
 
         let (name, shorts, help) = match iter.next() {
-            Some(Meta::Item(Item::Flag { name, shorts, help })) => (name, shorts, help),
+            Some(Meta::Item(item)) => match &**item {
+                Item::Flag { name, shorts, help } => (name, shorts, help),
+                _ => return meta,
+            },
             _ => return meta,
         };
 
-        while let Some(Meta::Item(Item::Positional {
-            metavar,
-            strict: _,
-            help,
-        })) = iter.next()
-        {
-            fields.push((*metavar, help.clone()));
+        while let Some(Meta::Item(item)) = iter.next() {
+            if let Item::Positional {
+                metavar,
+                strict: _,
+                help,
+            } = &**item
+            {
+                fields.push((*metavar, help.clone()));
+            } else {
+                break;
+            }
         }
         if iter.next().is_none() {
-            return Meta::Item(Item::MultiArg {
+            return Meta::from(Item::MultiArg {
                 name: *name,
                 shorts: shorts.clone(),
                 help: help.clone(),


Test patch:
diff --git a/tests/polymorph.rs b/tests/polymorph.rs
new file mode 100644
index 0000000..88cf415
--- /dev/null
+++ b/tests/polymorph.rs
@@ -0,0 +1,35 @@
+use std::str::FromStr;
+
+use bpaf::*;
+
+#[test]
+fn generic_argument_field() {
+    #[derive(Debug, Clone, Eq, PartialEq)]
+    struct Poly<T> {
+        field: T,
+    }
+
+    fn poly<T>(name: &'static str) -> impl Parser<Poly<T>>
+    where
+        T: FromStr + 'static,
+        <T as FromStr>::Err: std::fmt::Display,
+    {
+        let field = long(name).argument("ARG");
+        construct!(Poly { field })
+    }
+
+    let a = poly::<usize>("usize").optional();
+    let b = poly::<u32>("u32").optional();
+    let parser = construct!(a, b).to_options();
+
+    let r = parser.run_inner(Args::from(&["--usize", "12"])).unwrap();
+    assert_eq!(r, (Some(Poly { field: 12 }), None));
+
+    let r = parser.run_inner(Args::from(&["--u32", "12"])).unwrap();
+    assert_eq!(r, (None, Some(Poly { field: 12 })));
+
+    let r = parser
+        .run_inner(Args::from(&["--u32", "12", "--usize", "24"]))
+        .unwrap();
+    assert_eq!(r, (Some(Poly { field: 24 }), Some(Poly { field: 12 })));
+}
diff --git a/tests/tests.rs b/tests/tests.rs
index 6078f89..09b6310 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -861,7 +861,7 @@ fn long_path_in_construct() {
 
 #[test]
 fn hidden_env() {
-    let name = "BPAF_SECRET_API_KEY";
+    let name = "BPAF_SECRET_API_KEY2";
     let visible = long("key")
         .help("use this secret key\ntwo lines")
         .argument::<String>("KEY");
@@ -882,6 +882,12 @@ Available options:
 ";
 
     assert_eq!(help, expected);
+
+    let r = parser
+        .run_inner(Args::from(&[]))
+        .unwrap_err()
+        .unwrap_stderr();
+    assert_eq!(r, "env variable BPAF_SECRET_API_KEY2 is not set");
 }
 
 #[test]