rhg: add support for detailed exit code for ConfigParseError
authorPulkit Goyal <7895pulkit@gmail.com>
Sat, 20 Mar 2021 02:03:57 +0530
changeset 46820 821929d59e01
parent 46819 d4ba4d51f85f
child 46821 e8ae91b1a63d
rhg: add support for detailed exit code for ConfigParseError This patch adds basic support for detailed exit code to rhg with support for ConfigParseError. For now, if parsing the config results in error, we silently fallbacks to `false`. The python version in this case emits a traceback. Differential Revision: https://phab.mercurial-scm.org/D10253
rust/rhg/src/error.rs
rust/rhg/src/exitcode.rs
rust/rhg/src/main.rs
tests/test-config.t
tests/test-dispatch.t
--- a/rust/rhg/src/error.rs	Thu Mar 25 18:02:08 2021 +0100
+++ b/rust/rhg/src/error.rs	Sat Mar 20 02:03:57 2021 +0530
@@ -1,3 +1,4 @@
+use crate::exitcode;
 use crate::ui::utf8_to_local;
 use crate::ui::UiError;
 use crate::NoRepoInCwdError;
@@ -14,7 +15,10 @@
 #[derive(Debug)]
 pub enum CommandError {
     /// Exit with an error message and "standard" failure exit code.
-    Abort { message: Vec<u8> },
+    Abort {
+        message: Vec<u8>,
+        detailed_exit_code: exitcode::ExitCode,
+    },
 
     /// Exit with a failure exit code but no message.
     Unsuccessful,
@@ -28,11 +32,19 @@
 
 impl CommandError {
     pub fn abort(message: impl AsRef<str>) -> Self {
+        CommandError::abort_with_exit_code(message, exitcode::ABORT)
+    }
+
+    pub fn abort_with_exit_code(
+        message: impl AsRef<str>,
+        detailed_exit_code: exitcode::ExitCode,
+    ) -> Self {
         CommandError::Abort {
             // TODO: bytes-based (instead of Unicode-based) formatting
             // of error messages to handle non-UTF-8 filenames etc:
             // https://www.mercurial-scm.org/wiki/EncodingStrategy#Mixing_output
             message: utf8_to_local(message.as_ref()).into(),
+            detailed_exit_code: detailed_exit_code,
         }
     }
 
@@ -64,7 +76,10 @@
 
 impl From<ConfigValueParseError> for CommandError {
     fn from(error: ConfigValueParseError) -> Self {
-        CommandError::abort(error.to_string())
+        CommandError::abort_with_exit_code(
+            error.to_string(),
+            exitcode::CONFIG_ERROR_ABORT,
+        )
     }
 }
 
@@ -85,6 +100,7 @@
                     b"abort: repository {} not found",
                     get_bytes_from_path(at)
                 ),
+                detailed_exit_code: exitcode::ABORT,
             },
             RepoError::ConfigParseError(error) => error.into(),
             RepoError::Other(error) => error.into(),
@@ -100,6 +116,7 @@
                 b"abort: no repository found in '{}' (.hg not found)!",
                 get_bytes_from_path(cwd)
             ),
+            detailed_exit_code: exitcode::ABORT,
         }
     }
 }
@@ -132,6 +149,7 @@
                 line_message,
                 message
             ),
+            detailed_exit_code: exitcode::CONFIG_ERROR_ABORT,
         }
     }
 }
--- a/rust/rhg/src/exitcode.rs	Thu Mar 25 18:02:08 2021 +0100
+++ b/rust/rhg/src/exitcode.rs	Sat Mar 20 02:03:57 2021 +0530
@@ -6,6 +6,9 @@
 /// Generic abort
 pub const ABORT: ExitCode = 255;
 
+// Abort when there is a config related error
+pub const CONFIG_ERROR_ABORT: ExitCode = 30;
+
 /// Generic something completed but did not succeed
 pub const UNSUCCESSFUL: ExitCode = 1;
 
--- a/rust/rhg/src/main.rs	Thu Mar 25 18:02:08 2021 +0100
+++ b/rust/rhg/src/main.rs	Sat Mar 20 02:03:57 2021 +0530
@@ -82,7 +82,14 @@
     let blackbox = blackbox::Blackbox::new(&invocation, process_start_time)?;
     blackbox.log_command_start();
     let result = run(&invocation);
-    blackbox.log_command_end(exit_code(&result));
+    blackbox.log_command_end(exit_code(
+        &result,
+        // TODO: show a warning or combine with original error if `get_bool`
+        // returns an error
+        config
+            .get_bool(b"ui", b"detailed-exit-code")
+            .unwrap_or(false),
+    ));
     result
 }
 
@@ -114,6 +121,7 @@
                         error,
                         cwd.display()
                     ))),
+                    false,
                 )
             })
     });
@@ -125,7 +133,13 @@
             // "unsupported" error but that is not enforced by the type system.
             let on_unsupported = OnUnsupported::Abort;
 
-            exit(&initial_current_dir, &ui, on_unsupported, Err(error.into()))
+            exit(
+                &initial_current_dir,
+                &ui,
+                on_unsupported,
+                Err(error.into()),
+                false,
+            )
         });
 
     if let Some(repo_path_bytes) = &early_args.repo {
@@ -145,6 +159,11 @@
                         repo_path_bytes
                     ),
                 }),
+                // TODO: show a warning or combine with original error if
+                // `get_bool` returns an error
+                non_repo_config
+                    .get_bool(b"ui", b"detailed-exit-code")
+                    .unwrap_or(false),
             )
         }
     }
@@ -160,6 +179,11 @@
             &ui,
             OnUnsupported::from_config(&ui, &non_repo_config),
             Err(error.into()),
+            // TODO: show a warning or combine with original error if
+            // `get_bool` returns an error
+            non_repo_config
+                .get_bool(b"ui", b"detailed-exit-code")
+                .unwrap_or(false),
         ),
     };
 
@@ -176,13 +200,35 @@
         repo_result.as_ref(),
         config,
     );
-    exit(&initial_current_dir, &ui, on_unsupported, result)
+    exit(
+        &initial_current_dir,
+        &ui,
+        on_unsupported,
+        result,
+        // TODO: show a warning or combine with original error if `get_bool`
+        // returns an error
+        config
+            .get_bool(b"ui", b"detailed-exit-code")
+            .unwrap_or(false),
+    )
 }
 
-fn exit_code(result: &Result<(), CommandError>) -> i32 {
+fn exit_code(
+    result: &Result<(), CommandError>,
+    use_detailed_exit_code: bool,
+) -> i32 {
     match result {
         Ok(()) => exitcode::OK,
-        Err(CommandError::Abort { .. }) => exitcode::ABORT,
+        Err(CommandError::Abort {
+            message: _,
+            detailed_exit_code,
+        }) => {
+            if use_detailed_exit_code {
+                *detailed_exit_code
+            } else {
+                exitcode::ABORT
+            }
+        }
         Err(CommandError::Unsuccessful) => exitcode::UNSUCCESSFUL,
 
         // Exit with a specific code and no error message to let a potential
@@ -198,6 +244,7 @@
     ui: &Ui,
     mut on_unsupported: OnUnsupported,
     result: Result<(), CommandError>,
+    use_detailed_exit_code: bool,
 ) -> ! {
     if let (
         OnUnsupported::Fallback { executable },
@@ -238,18 +285,22 @@
             }
         }
     }
-    exit_no_fallback(ui, on_unsupported, result)
+    exit_no_fallback(ui, on_unsupported, result, use_detailed_exit_code)
 }
 
 fn exit_no_fallback(
     ui: &Ui,
     on_unsupported: OnUnsupported,
     result: Result<(), CommandError>,
+    use_detailed_exit_code: bool,
 ) -> ! {
     match &result {
         Ok(_) => {}
         Err(CommandError::Unsuccessful) => {}
-        Err(CommandError::Abort { message }) => {
+        Err(CommandError::Abort {
+            message,
+            detailed_exit_code: _,
+        }) => {
             if !message.is_empty() {
                 // Ignore errors when writing to stderr, we’re already exiting
                 // with failure code so there’s not much more we can do.
@@ -269,7 +320,7 @@
             }
         }
     }
-    std::process::exit(exit_code(&result))
+    std::process::exit(exit_code(&result, use_detailed_exit_code))
 }
 
 macro_rules! subcommands {
@@ -411,6 +462,7 @@
                                 "abort: 'rhg.on-unsupported=fallback' without \
                                 'rhg.fallback-executable' set."
                             )),
+                            false,
                         )
                     })
                     .to_owned(),
--- a/tests/test-config.t	Thu Mar 25 18:02:08 2021 +0100
+++ b/tests/test-config.t	Sat Mar 20 02:03:57 2021 +0530
@@ -3,8 +3,6 @@
 
 Invalid syntax: no value
 
-TODO: add rhg support for detailed exit codes
-#if no-rhg
   $ cat > .hg/hgrc << EOF
   > novaluekey
   > EOF
@@ -37,7 +35,6 @@
   $ hg showconfig
   config error at $TESTTMP/.hg/hgrc:1: unexpected leading whitespace:  [section]
   [30]
-#endif
 
 Reset hgrc
 
--- a/tests/test-dispatch.t	Thu Mar 25 18:02:08 2021 +0100
+++ b/tests/test-dispatch.t	Sat Mar 20 02:03:57 2021 +0530
@@ -90,12 +90,9 @@
 
   $ mkdir -p badrepo/.hg
   $ echo 'invalid-syntax' > badrepo/.hg/hgrc
-TODO: add rhg support for detailed exit codes
-#if no-rhg
   $ hg log -b -Rbadrepo default
   config error at badrepo/.hg/hgrc:1: invalid-syntax
   [30]
-#endif
 
   $ hg log -b --cwd=inexistent default
   abort: $ENOENT$: 'inexistent'