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
use std::{fmt, io, path::PathBuf, process::ExitStatus, string::FromUtf8Error};
use crate::Cmd;
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub struct Error {
repr: Box<Repr>,
}
enum Repr {
CmdError(CmdError),
FsError(FsError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn errstr(err: &io::Error) -> String {
let mut res = err.to_string();
if res.is_char_boundary(1) {
res[..1].make_ascii_lowercase();
}
res
}
match &*self.repr {
Repr::CmdError(err) => match &err.kind {
CmdErrorKind::NonZeroStatus(status) => {
write!(f, "command `{}` failed, {}", err.cmd, status)
}
CmdErrorKind::Io(io_err) => {
if io_err.kind() == io::ErrorKind::NotFound {
write!(f, "command not found: `{}`", err.cmd.args[0].to_string_lossy())
} else {
write!(f, "command `{}` failed, {}", err.cmd, errstr(io_err))
}
}
CmdErrorKind::NonUtf8Output(utf8_err) => {
write!(f, "command `{}` produced invalid utf8, {}", err.cmd, utf8_err)
}
},
Repr::FsError(err) => write!(f, "`{}`: {}", err.path.display(), errstr(&err.io_err)),
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl std::error::Error for Error {}
pub(crate) struct CmdError {
cmd: Cmd,
kind: CmdErrorKind,
}
pub(crate) enum CmdErrorKind {
NonZeroStatus(ExitStatus),
Io(io::Error),
NonUtf8Output(FromUtf8Error),
}
impl CmdErrorKind {
pub(crate) fn err(self, cmd: Cmd) -> Error {
Error { repr: Box::new(Repr::CmdError(CmdError { cmd, kind: self })) }
}
}
pub(crate) struct FsError {
path: PathBuf,
io_err: io::Error,
}
pub(crate) fn fs_err(path: PathBuf, io_err: io::Error) -> Error {
Error { repr: Box::new(Repr::FsError(FsError { path, io_err })) }
}