generated from tweag/project
-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy patherror.rs
170 lines (144 loc) · 5.06 KB
/
error.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
use std::{error, fmt, io, process::ExitCode, result, sync};
use topiary::FormatterError;
/// A convenience wrapper around `std::result::Result<T, TopiaryError>`.
pub type CLIResult<T> = result::Result<T, TopiaryError>;
/// The errors that can be raised by either the Topiary CLI, or passed through by the formatter
/// library code. This acts as a supertype of `FormatterError`, with additional members to denote
/// CLI-specific failures.
#[derive(Debug)]
pub enum TopiaryError {
Lib(FormatterError),
Bin(String, Option<CLIError>),
}
/// A subtype of `TopiaryError::Bin`
#[derive(Debug)]
pub enum CLIError {
IOError(io::Error),
Generic(Box<dyn error::Error>),
Multiple,
}
/// # Safety
///
/// Something can safely be Send unless it shares mutable state with something
/// else without enforcing exclusive access to it. TopiaryError does not have a
/// mutable state.
unsafe impl Send for TopiaryError {}
/// # Safety
///
/// Something can safely be Sync if and only if no other &TopiaryError can write
/// to it. Since our TopiaryError contains no mutable data, TopiaryError is Sync.
unsafe impl Sync for TopiaryError {}
impl fmt::Display for TopiaryError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Lib(error) => write!(f, "{error}"),
Self::Bin(message, _) => write!(f, "{message}"),
}
}
}
impl error::Error for TopiaryError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::Lib(error) => error.source(),
Self::Bin(_, Some(CLIError::IOError(error))) => Some(error),
Self::Bin(_, Some(CLIError::Generic(error))) => error.source(),
Self::Bin(_, Some(CLIError::Multiple)) => None,
Self::Bin(_, None) => None,
}
}
}
impl From<TopiaryError> for ExitCode {
fn from(e: TopiaryError) -> Self {
let exit_code = match e {
// Multiple errors: Exit 9
TopiaryError::Bin(_, Some(CLIError::Multiple)) => 9,
// Idempotency parsing errors: Exit 8
TopiaryError::Lib(FormatterError::IdempotenceParsing(_)) => 8,
// Idempotency errors: Exit 7
TopiaryError::Lib(FormatterError::Idempotence) => 7,
// Language detection errors: Exit 6
TopiaryError::Lib(FormatterError::LanguageDetection(_, _)) => 6,
// Parsing errors: Exit 5
TopiaryError::Lib(FormatterError::Parsing { .. }) => 5,
// Query errors: Exit 4
TopiaryError::Lib(FormatterError::Query(_, _)) => 4,
// I/O errors: Exit 3
TopiaryError::Lib(FormatterError::Io(_))
| TopiaryError::Bin(_, Some(CLIError::IOError(_))) => 3,
// Bad arguments: Exit 2
// (Handled by clap: https://github.com/clap-rs/clap/issues/3426)
// Anything else: Exit 1
_ => 1,
};
ExitCode::from(exit_code)
}
}
impl From<FormatterError> for TopiaryError {
fn from(e: FormatterError) -> Self {
Self::Lib(e)
}
}
impl From<io::Error> for TopiaryError {
fn from(e: io::Error) -> Self {
match e.kind() {
io::ErrorKind::NotFound => {
Self::Bin("File not found".into(), Some(CLIError::IOError(e)))
}
_ => Self::Bin(
"Could not read or write to file".into(),
Some(CLIError::IOError(e)),
),
}
}
}
impl From<tempfile::PersistError> for TopiaryError {
fn from(e: tempfile::PersistError) -> Self {
Self::Bin(
"Could not persist output to disk".into(),
Some(CLIError::IOError(e.error)),
)
}
}
// We only have to deal with io::BufWriter<crate::output::OutputFile>,
// but the genericised code is clearer
impl<W> From<io::IntoInnerError<W>> for TopiaryError
where
W: io::Write + fmt::Debug + Send + 'static,
{
fn from(e: io::IntoInnerError<W>) -> Self {
Self::Bin(
"Could not flush internal buffer".into(),
Some(CLIError::Generic(Box::new(e))),
)
}
}
impl<T> From<sync::PoisonError<T>> for TopiaryError {
fn from(_: sync::PoisonError<T>) -> Self {
// TODO Pass the error into `CLIError::Generic`, without invalidating lifetime constraints
Self::Bin("Could not acquire cache lock".into(), None)
}
}
impl From<toml::de::Error> for TopiaryError {
fn from(e: toml::de::Error) -> Self {
TopiaryError::Bin(
"Could not parse configuration".into(),
Some(CLIError::Generic(Box::new(e))),
)
}
}
impl From<serde_toml_merge::Error> for TopiaryError {
fn from(e: serde_toml_merge::Error) -> Self {
TopiaryError::Bin(
format!("Could not collate configuration from {}", e.path),
None,
)
}
}
impl From<tokio::task::JoinError> for TopiaryError {
fn from(e: tokio::task::JoinError) -> Self {
TopiaryError::Bin(
"Could not join parallel formatting tasks".into(),
Some(CLIError::Generic(Box::new(e))),
)
}
}