Skip to main content

semora_cli/
exit.rs

1//! Exit codes. `wait` defines them in the spec; every command uses the same ones.
2
3use std::fmt;
4
5pub const OK: i32 = 0;
6pub const ERROR: i32 = 1;
7pub const TIMEOUT: i32 = 2;
8pub const AUTH: i32 = 3;
9pub const NOT_FOUND: i32 = 4;
10/// A push refused because the server has a revision this checkout didn't start from.
11pub const STALE: i32 = 5;
12
13/// An error that carries its exit code.
14#[derive(Debug)]
15pub struct Exit {
16    pub code: i32,
17    pub message: String,
18}
19
20impl fmt::Display for Exit {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        f.write_str(&self.message)
23    }
24}
25
26impl std::error::Error for Exit {}
27
28pub fn auth(message: impl Into<String>) -> anyhow::Error {
29    Exit {
30        code: AUTH,
31        message: message.into(),
32    }
33    .into()
34}
35
36pub fn not_found(message: impl Into<String>) -> anyhow::Error {
37    Exit {
38        code: NOT_FOUND,
39        message: message.into(),
40    }
41    .into()
42}
43
44pub fn timeout(message: impl Into<String>) -> anyhow::Error {
45    Exit {
46        code: TIMEOUT,
47        message: message.into(),
48    }
49    .into()
50}
51
52pub fn stale(message: impl Into<String>) -> anyhow::Error {
53    Exit {
54        code: STALE,
55        message: message.into(),
56    }
57    .into()
58}
59
60/// The exit code an error maps to.
61pub fn code_of(err: &anyhow::Error) -> i32 {
62    err.downcast_ref::<Exit>().map(|e| e.code).unwrap_or(ERROR)
63}
64
65pub fn is_not_found(err: &anyhow::Error) -> bool {
66    code_of(err) == NOT_FOUND
67}
68
69pub fn is_stale(err: &anyhow::Error) -> bool {
70    code_of(err) == STALE
71}
72
73/// The message of an [`Exit`], or the whole error.
74pub fn message_of(err: &anyhow::Error) -> String {
75    match err.downcast_ref::<Exit>() {
76        Some(x) => x.message.clone(),
77        None => format!("{err:#}"),
78    }
79}
80
81/// The server answered 409.
82#[derive(Debug)]
83pub struct Conflict(pub String);
84
85impl fmt::Display for Conflict {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.write_str(&self.0)
88    }
89}
90
91impl std::error::Error for Conflict {}
92
93pub fn is_conflict(err: &anyhow::Error) -> bool {
94    err.downcast_ref::<Conflict>().is_some()
95}