Repository: clap-rs/clap
Pull request #2399 (2021-03-09 16:41:10)

Implement NoEquals error
Splitted from #2360.


Patch:
diff --git a/src/build/app/settings.rs b/src/build/app/settings.rs
index e10d4d0b..9a9dec47 100644
--- a/src/build/app/settings.rs
+++ b/src/build/app/settings.rs
@@ -1041,6 +1041,7 @@ pub enum AppSettings {
     NoAutoVersion,
 
     #[doc(hidden)]
+    /// If the user already passed '--'. Meaning only positional args follow.
     TrailingValues,
 
     #[doc(hidden)]
diff --git a/src/build/arg/mod.rs b/src/build/arg/mod.rs
index a5ed40c9..eea7a24f 100644
--- a/src/build/arg/mod.rs
+++ b/src/build/arg/mod.rs
@@ -3425,8 +3425,8 @@ impl<'help> Arg<'help> {
     /// assert!(res.is_ok());
     /// ```
     ///
-    /// Setting [`RequireEquals`] and *not* supplying the equals will cause an error
-    /// unless [`ArgSettings::EmptyValues`] is set.
+    /// Setting [`RequireEquals`] and *not* supplying the equals will cause an
+    /// error.
     ///
     /// ```rust
     /// # use clap::{App, Arg, ErrorKind, ArgSettings};
@@ -3440,11 +3440,9 @@ impl<'help> Arg<'help> {
     ///     ]);
     ///
     /// assert!(res.is_err());
-    /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
+    /// assert_eq!(res.unwrap_err().kind, ErrorKind::NoEquals);
     /// ```
     /// [`RequireEquals`]: ./enum.ArgSettings.html#variant.RequireEquals
-    /// [`ArgSettings::EmptyValues`]: ./enum.ArgSettings.html#variant.EmptyValues
-    /// [`ArgSettings::EmptyValues`]: ./enum.ArgSettings.html#variant.TakesValue
     #[inline]
     pub fn require_equals(self, r: bool) -> Self {
         if r {
diff --git a/src/parse/errors.rs b/src/parse/errors.rs
index 4099cc55..c69d42ec 100644
--- a/src/parse/errors.rs
+++ b/src/parse/errors.rs
@@ -123,6 +123,22 @@ pub enum ErrorKind {
     /// ```
     EmptyValue,
 
+    /// Occurs when the user doesn't use equals for an option that requres equal
+    /// sign to provide values.
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg, ErrorKind, ArgSettings};
+    /// let res = App::new("prog")
+    ///     .arg(Arg::new("color")
+    ///          .setting(ArgSettings::TakesValue)
+    ///          .setting(ArgSettings::RequireEquals)
+    ///          .long("color"))
+    ///     .try_get_matches_from(vec!["prog", "--color", "red"]);
+    /// assert!(res.is_err());
+    /// assert_eq!(res.unwrap_err().kind, ErrorKind::NoEquals);
+    /// ```
+    NoEquals,
+
     /// Occurs when the user provides a value for an argument with a custom validation and the
     /// value fails that validation.
     ///
@@ -527,6 +543,25 @@ impl Error {
         }
     }
 
+    pub(crate) fn no_equals(arg: &Arg, usage: String, color: ColorChoice) -> Self {
+        let mut c = Colorizer::new(true, color);
+        let arg = arg.to_string();
+
+        start_error(&mut c, "Equal sign is needed when assigning values to '");
+        c.warning(&arg);
+        c.none("'.");
+
+        put_usage(&mut c, usage);
+        try_help(&mut c);
+
+        Error {
+            message: c,
+            kind: ErrorKind::NoEquals,
+            info: vec![arg],
+            source: None,
+        }
+    }
+
     pub(crate) fn invalid_value<G>(
         bad_val: String,
         good_vals: &[G],
diff --git a/src/parse/parser.rs b/src/parse/parser.rs
index e448a4a9..90705b5f 100644
--- a/src/parse/parser.rs
+++ b/src/parse/parser.rs
@@ -1176,24 +1176,32 @@ impl<'help, 'app> Parser<'help, 'app> {
                 fv.starts_with("=")
             );
             self.add_val_to_arg(opt, v, matcher, ValueType::CommandLine, false);
-        } else if require_equals && !empty_vals && !min_vals_zero {
-            debug!("None, but requires equals...Error");
-            return Err(ClapError::empty_value(
-                opt,
-                Usage::new(self).create_usage_with_title(&[]),
-                self.app.color(),
-            ));
-        } else if require_equals && min_vals_zero {
-            debug!("None and requires equals, but min_vals == 0");
-            if !opt.default_missing_vals.is_empty() {
-                debug!("Parser::parse_opt: has default_missing_vals");
-                let vals = opt
-                    .default_missing_vals
-                    .iter()
-                    .map(OsString::from)
-                    .collect();
-                self.add_multiple_vals_to_arg(opt, vals, matcher, ValueType::CommandLine, false);
-            };
+        } else if require_equals {
+            if min_vals_zero {
+                debug!("Requires equals, but min_vals == 0");
+                if !opt.default_missing_vals.is_empty() {
+                    debug!("Parser::parse_opt: has default_missing_vals");
+                    let vals = opt
+                        .default_missing_vals
+                        .iter()
+                        .map(OsString::from)
+                        .collect();
+                    self.add_multiple_vals_to_arg(
+                        opt,
+                        vals,
+                        matcher,
+                        ValueType::CommandLine,
+                        false,
+                    );
+                };
+            } else {
+                debug!("Requires equals but not provided. Error.");
+                return Err(ClapError::no_equals(
+                    opt,
+                    Usage::new(self).create_usage_with_title(&[]),
+                    self.app.color(),
+                ));
+            }
         } else {
             debug!("None");
         }


Test patch:
diff --git a/clap_derive/tests/non_literal_attributes.rs b/clap_derive/tests/non_literal_attributes.rs
index 72e8b865..5a342c2c 100644
--- a/clap_derive/tests/non_literal_attributes.rs
+++ b/clap_derive/tests/non_literal_attributes.rs
@@ -12,7 +12,7 @@
 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
 // MIT/Apache 2.0 license.
 
-use clap::{AppSettings, Clap};
+use clap::{AppSettings, Clap, ErrorKind};
 use std::num::ParseIntError;
 
 pub const DISPLAY_ORDER: usize = 2;
@@ -121,6 +121,7 @@ fn test_bool() {
     );
     let result = Opt::try_parse_from(&["test", "-l", "1", "--x", "1"]);
     assert!(result.is_err());
+    assert_eq!(result.unwrap_err().kind, ErrorKind::NoEquals);
 }
 
 fn parse_hex(input: &str) -> Result<u64, ParseIntError> {
diff --git a/tests/opts.rs b/tests/opts.rs
index 906a6c5c..3465a063 100644
--- a/tests/opts.rs
+++ b/tests/opts.rs
@@ -39,7 +39,30 @@ fn require_equals_fail() {
         )
         .try_get_matches_from(vec!["prog", "--config", "file.conf"]);
     assert!(res.is_err());
-    assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue);
+    assert_eq!(res.unwrap_err().kind, ErrorKind::NoEquals);
+}
+
+#[test]
+fn require_equals_fail_message() {
+    static NO_EQUALS: &str =
+        "error: Equal sign is needed when assigning values to '--config=<cfg>'.
+
+USAGE:
+    prog [OPTIONS]
+
+For more information try --help";
+    let app = App::new("prog").arg(
+        Arg::new("cfg")
+            .setting(ArgSettings::RequireEquals)
+            .setting(ArgSettings::TakesValue)
+            .long("config"),
+    );
+    assert!(utils::compare_output(
+        app,
+        "prog --config file.conf",
+        NO_EQUALS,
+        true
+    ));
 }
 
 #[test]