Repository: pacak/bpaf
Pull request #201 (2023-04-16 00:56:53)

Bugfix: fallback/fallback_with should not interrupt the completion
Fixes a problem described in https://github.com/pacak/bpaf/discussions/197


Patch:
diff --git a/src/structs.rs b/src/structs.rs
index cd81e48..08451d4 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -21,13 +21,23 @@ where
     E: ToString,
 {
     fn eval(&self, args: &mut Args) -> Result<T, Error> {
-        match self.inner.eval(args) {
-            Ok(ok) => Ok(ok),
-            e @ Err(Error::Message(_, false) | Error::ParseFailure(_)) => e,
-            Err(Error::Missing(_) | Error::Message(_, true)) => match (self.fallback)() {
-                Ok(ok) => Ok(ok),
-                Err(e) => Err(Error::Message(e.to_string(), false)),
-            },
+        let mut clone = args.clone();
+        match self.inner.eval(&mut clone) {
+            Ok(ok) => {
+                std::mem::swap(args, &mut clone);
+                Ok(ok)
+            }
+            Err(e) => {
+                #[cfg(feature = "autocomplete")]
+                args.swap_comps(&mut clone);
+                match e {
+                    Error::Message(_, false) | Error::ParseFailure(_) => Err(e),
+                    Error::Missing(_) | Error::Message(_, true) => match (self.fallback)() {
+                        Ok(ok) => Ok(ok),
+                        Err(e) => Err(Error::Message(e.to_string(), false)),
+                    },
+                }
+            }
         }
     }
 
@@ -369,20 +379,18 @@ where
     T: Clone,
 {
     fn eval(&self, args: &mut Args) -> Result<T, Error> {
-        {
-            let parser = &self.inner;
-            let value = &self.value;
-            let mut clone = args.clone();
-            match parser.eval(&mut clone) {
-                Ok(ok) => {
-                    std::mem::swap(args, &mut clone);
-                    Ok(ok)
-                }
-                e @ Err(Error::Message(_, false) | Error::ParseFailure(_)) => e,
-                Err(Error::Missing(_) | Error::Message(_, true)) => {
-                    #[cfg(feature = "autocomplete")]
-                    args.swap_comps(&mut clone);
-                    Ok(value.clone())
+        let mut clone = args.clone();
+        match self.inner.eval(&mut clone) {
+            Ok(ok) => {
+                std::mem::swap(args, &mut clone);
+                Ok(ok)
+            }
+            Err(e) => {
+                #[cfg(feature = "autocomplete")]
+                args.swap_comps(&mut clone);
+                match e {
+                    Error::Message(_, false) | Error::ParseFailure(_) => Err(e),
+                    Error::Missing(_) | Error::Message(_, true) => Ok(self.value.clone()),
                 }
             }
         }


Test patch:
diff --git a/tests/completion.rs b/tests/completion.rs
index 48ea63a..bb227c6 100644
--- a/tests/completion.rs
+++ b/tests/completion.rs
@@ -1145,3 +1145,26 @@ fn generate_unparseable_items() {
         4,
     );
 }
+
+#[test]
+fn complete_with_fallback() {
+    let parser = long("name")
+        .argument::<String>("NAME")
+        .complete(test_completer_descr)
+        .parse(|x| x.parse::<u16>())
+        .fallback(10)
+        .to_options();
+
+    test_comp_v3(
+        &parser,
+        &["--name", ""],
+        &[
+            "literal\talpha\tshow\talpha    alpha",
+            "literal\tbeta\tshow\tbeta    beta",
+            "literal\tbanana\tshow\tbanana    banana",
+            "literal\tcat\tshow\tcat    cat",
+            "literal\tdurian\tshow\tdurian    durian",
+        ],
+        4,
+    );
+}