1use camino::{Utf8Path, Utf8PathBuf};
5use hex::FromHexError;
6use serde::{de, ser};
7use std::{borrow::Cow, error, fmt, io, process::ExitStatus, result, str::Utf8Error};
8
9pub type Result<T, E = SystemError> = result::Result<T, E>;
11
12#[derive(Debug)]
14#[non_exhaustive]
15pub enum SystemError {
16 CwdNotInProjectRoot {
17 current_dir: Utf8PathBuf,
18 project_root: &'static Utf8Path,
19 },
20 Exec {
21 cmd: &'static str,
22 status: ExitStatus,
23 },
24 GitRoot(Cow<'static, str>),
25 FromHex {
26 context: Cow<'static, str>,
27 err: FromHexError,
28 },
29 Guppy {
30 context: Cow<'static, str>,
31 err: guppy::Error,
32 },
33 Io {
34 context: Cow<'static, str>,
35 err: io::Error,
36 },
37 NonUtf8Path {
38 path: Vec<u8>,
39 err: Utf8Error,
40 },
41 Serde {
42 context: Cow<'static, str>,
43 err: Box<dyn error::Error + Send + Sync>,
44 },
45}
46
47impl SystemError {
48 pub fn io(context: impl Into<Cow<'static, str>>, err: io::Error) -> Self {
49 SystemError::Io {
50 context: context.into(),
51 err,
52 }
53 }
54
55 pub fn guppy(context: impl Into<Cow<'static, str>>, err: guppy::Error) -> Self {
56 SystemError::Guppy {
57 context: context.into(),
58 err,
59 }
60 }
61
62 pub fn git_root(msg: impl Into<Cow<'static, str>>) -> Self {
63 SystemError::GitRoot(msg.into())
64 }
65
66 pub fn from_hex(context: impl Into<Cow<'static, str>>, err: FromHexError) -> Self {
67 SystemError::FromHex {
68 context: context.into(),
69 err,
70 }
71 }
72
73 pub fn de(
74 context: impl Into<Cow<'static, str>>,
75 err: impl de::Error + Send + Sync + 'static,
76 ) -> Self {
77 SystemError::Serde {
78 context: context.into(),
79 err: Box::new(err),
80 }
81 }
82
83 pub fn ser(
84 context: impl Into<Cow<'static, str>>,
85 err: impl ser::Error + Send + Sync + 'static,
86 ) -> Self {
87 SystemError::Serde {
88 context: context.into(),
89 err: Box::new(err),
90 }
91 }
92}
93
94impl fmt::Display for SystemError {
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 match self {
97 SystemError::CwdNotInProjectRoot {
98 current_dir,
99 project_root,
100 } => write!(
101 f,
102 "current dir {} not in project root {}",
103 current_dir, project_root,
104 ),
105 SystemError::Exec { cmd, status } => match status.code() {
106 Some(code) => write!(f, "'{}' failed with exit code {}", cmd, code),
107 None => write!(f, "'{}' terminated by signal", cmd),
108 },
109 SystemError::GitRoot(s) => write!(f, "git root error: {}", s),
110 SystemError::NonUtf8Path { path, .. } => {
111 write!(f, "non-UTF-8 path \"{}\"", String::from_utf8_lossy(path))
112 }
113 SystemError::FromHex { context, .. }
114 | SystemError::Io { context, .. }
115 | SystemError::Serde { context, .. }
116 | SystemError::Guppy { context, .. } => write!(f, "while {}", context),
117 }
118 }
119}
120
121impl error::Error for SystemError {
122 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
123 match self {
124 SystemError::CwdNotInProjectRoot { .. }
125 | SystemError::Exec { .. }
126 | SystemError::GitRoot(_) => None,
127 SystemError::FromHex { err, .. } => Some(err),
128 SystemError::Io { err, .. } => Some(err),
129 SystemError::Guppy { err, .. } => Some(err),
130 SystemError::NonUtf8Path { err, .. } => Some(err),
131 SystemError::Serde { err, .. } => Some(err.as_ref()),
132 }
133 }
134}