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
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::NonUtf8Stdout(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),
    NonUtf8Stdout(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 })) }
}