1use std::fmt;
8
9#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15#[non_exhaustive]
16pub enum ExitCategory {
17 Success,
19 Usage,
21 ConfigurationInvalid,
23 GuardRejected,
25 TargetNotFound,
27 OptimisticConflict,
29 OutcomeUnknown,
34 RepositoryUnavailable,
36 ConfirmationRequired,
38 DeadlineExceeded,
40 OutputFailure,
42 Internal,
44}
45
46impl ExitCategory {
47 #[must_use]
49 pub const fn code(self) -> u8 {
50 match self {
51 Self::Success => 0,
52 Self::Usage => 1,
53 Self::ConfigurationInvalid => 2,
54 Self::GuardRejected => 3,
55 Self::TargetNotFound => 4,
56 Self::OptimisticConflict => 5,
57 Self::OutcomeUnknown => 6,
58 Self::RepositoryUnavailable => 7,
59 Self::ConfirmationRequired => 8,
60 Self::DeadlineExceeded => 9,
61 Self::OutputFailure => 10,
62 Self::Internal => 70,
63 }
64 }
65
66 #[must_use]
68 pub const fn as_str(self) -> &'static str {
69 match self {
70 Self::Success => "SUCCESS",
71 Self::Usage => "USAGE",
72 Self::ConfigurationInvalid => "CONFIGURATION_INVALID",
73 Self::GuardRejected => "GUARD_REJECTED",
74 Self::TargetNotFound => "TARGET_NOT_FOUND",
75 Self::OptimisticConflict => "OPTIMISTIC_CONFLICT",
76 Self::OutcomeUnknown => "OUTCOME_UNKNOWN",
77 Self::RepositoryUnavailable => "REPOSITORY_UNAVAILABLE",
78 Self::ConfirmationRequired => "CONFIRMATION_REQUIRED",
79 Self::DeadlineExceeded => "DEADLINE_EXCEEDED",
80 Self::OutputFailure => "OUTPUT_FAILURE",
81 Self::Internal => "INTERNAL",
82 }
83 }
84
85 #[must_use]
87 pub const fn outcome(self) -> Outcome {
88 match self {
89 Self::Success => Outcome::Success,
90 Self::GuardRejected | Self::ConfirmationRequired => Outcome::Rejected,
91 Self::OptimisticConflict => Outcome::Conflict,
92 Self::OutcomeUnknown => Outcome::Unknown,
93 Self::Usage
94 | Self::ConfigurationInvalid
95 | Self::TargetNotFound
96 | Self::RepositoryUnavailable
97 | Self::DeadlineExceeded
98 | Self::OutputFailure
99 | Self::Internal => Outcome::Error,
100 }
101 }
102
103 #[must_use]
108 pub const fn all() -> &'static [Self] {
109 &[
110 Self::Success,
111 Self::Usage,
112 Self::ConfigurationInvalid,
113 Self::GuardRejected,
114 Self::TargetNotFound,
115 Self::OptimisticConflict,
116 Self::OutcomeUnknown,
117 Self::RepositoryUnavailable,
118 Self::ConfirmationRequired,
119 Self::DeadlineExceeded,
120 Self::OutputFailure,
121 Self::Internal,
122 ]
123 }
124}
125
126impl fmt::Display for ExitCategory {
127 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
128 formatter.write_str(self.as_str())
129 }
130}
131
132#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
134#[non_exhaustive]
135pub enum Outcome {
136 Success,
138 Rejected,
140 Conflict,
142 Unknown,
144 Error,
146}
147
148impl Outcome {
149 #[must_use]
151 pub const fn as_str(self) -> &'static str {
152 match self {
153 Self::Success => "success",
154 Self::Rejected => "rejected",
155 Self::Conflict => "conflict",
156 Self::Unknown => "unknown",
157 Self::Error => "error",
158 }
159 }
160}
161
162impl fmt::Display for Outcome {
163 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
164 formatter.write_str(self.as_str())
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 #![allow(clippy::expect_used, clippy::panic)]
171
172 use super::ExitCategory;
173
174 #[test]
175 fn codes_are_unique_and_ordered() {
176 let mut seen = Vec::new();
177 for category in ExitCategory::all() {
178 let code = category.code();
179 assert!(!seen.contains(&code), "exit code {code} is reused");
180 seen.push(code);
181 }
182 }
183
184 #[test]
185 fn published_codes_never_change() {
186 assert_eq!(ExitCategory::Success.code(), 0);
187 assert_eq!(ExitCategory::Usage.code(), 1);
188 assert_eq!(ExitCategory::ConfigurationInvalid.code(), 2);
189 assert_eq!(ExitCategory::GuardRejected.code(), 3);
190 assert_eq!(ExitCategory::TargetNotFound.code(), 4);
191 assert_eq!(ExitCategory::OptimisticConflict.code(), 5);
192 assert_eq!(ExitCategory::OutcomeUnknown.code(), 6);
193 assert_eq!(ExitCategory::RepositoryUnavailable.code(), 7);
194 assert_eq!(ExitCategory::ConfirmationRequired.code(), 8);
195 assert_eq!(ExitCategory::DeadlineExceeded.code(), 9);
196 assert_eq!(ExitCategory::OutputFailure.code(), 10);
197 assert_eq!(ExitCategory::Internal.code(), 70);
198 }
199}