Skip to main content

ctx/
exit.rs

1//! Exit-code convention shared by all ctx commands.
2//!
3//! ctx uses a three-way exit-code convention (similar to `grep` or linters),
4//! plus one reserved code for harness compatibility checks:
5//!
6//! | Code | Meaning                                                    |
7//! |------|------------------------------------------------------------|
8//! | 0    | Success, no findings                                       |
9//! | 1    | Command ran successfully but produced findings             |
10//! | 2    | Operational error (bad arguments, missing index, IO, ...)  |
11//! | 3    | Version requirement not met (`ctx harness compat` only)    |
12//!
13//! Commands return `Ok(Outcome::Clean)` or `Ok(Outcome::Findings)`;
14//! any `Err(_)` maps to exit code 2 in `main`.
15
16/// The successful outcome of a command, mapped to a process exit code.
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Outcome {
19    /// Command succeeded with nothing to report (exit code 0).
20    Clean,
21    /// Command succeeded but produced findings (exit code 1).
22    ///
23    /// Used by quality commands (e.g. a check that flags issues) so scripts
24    /// and CI can distinguish "found problems" from "failed to run".
25    Findings,
26    /// The running binary does not satisfy a required version (exit code 3).
27    ///
28    /// Exit code 3 is reserved exclusively for `ctx harness compat` so that
29    /// generated hook scripts can distinguish "binary too old for these
30    /// templates" from findings (1) and operational errors (2). No other
31    /// command may return this outcome.
32    CompatMismatch,
33}
34
35impl Outcome {
36    /// The process exit code for this outcome.
37    pub fn code(self) -> u8 {
38        match self {
39            Outcome::Clean => 0,
40            Outcome::Findings => 1,
41            Outcome::CompatMismatch => 3,
42        }
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn test_outcome_codes() {
52        assert_eq!(Outcome::Clean.code(), 0);
53        assert_eq!(Outcome::Findings.code(), 1);
54        assert_ne!(Outcome::Clean, Outcome::Findings);
55    }
56
57    #[test]
58    fn test_compat_mismatch_is_exit_code_3() {
59        assert_eq!(Outcome::CompatMismatch.code(), 3);
60    }
61}