Skip to main content

tess/
error.rs

1use std::fmt;
2use std::path::PathBuf;
3
4#[derive(Debug)]
5pub enum Error {
6    OpenFile { path: PathBuf, source: std::io::Error },
7    NotAFile { path: PathBuf },
8    NoInput,
9    Runtime(String),
10    /// Tags file specified by `-T` or required by `-t` could not be opened.
11    TagFileNotFound,
12    /// Tags file is malformed: (reason, path, line number).
13    TagFileParse(String, std::path::PathBuf, usize),
14}
15
16impl Error {
17    pub fn exit_code(&self) -> i32 {
18        match self {
19            Error::OpenFile { .. }
20            | Error::NotAFile { .. }
21            | Error::NoInput
22            | Error::TagFileNotFound
23            | Error::TagFileParse(..) => 1,
24            Error::Runtime(_) => 2,
25        }
26    }
27}
28
29impl fmt::Display for Error {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Error::OpenFile { path, source } => {
33                write!(f, "tess: {}: {}", path.display(), source)
34            }
35            Error::NotAFile { path } => {
36                write!(f, "tess: {}: not a regular file", path.display())
37            }
38            Error::NoInput => write!(f, "tess: no input (specify a file or pipe stdin)"),
39            Error::Runtime(msg) => write!(f, "tess: {}", msg),
40            Error::TagFileNotFound => {
41                write!(f, "tags file not found")
42            }
43            Error::TagFileParse(reason, path, line) => {
44                write!(f, "tags file parse error: {reason} at {}:{line}", path.display())
45            }
46        }
47    }
48}
49
50impl std::error::Error for Error {}
51
52pub type Result<T> = std::result::Result<T, Error>;
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn display_wraps_io_error_with_context() {
60        let io = std::io::Error::new(std::io::ErrorKind::NotFound, "no such file");
61        let e = Error::OpenFile { path: "/nope".into(), source: io };
62        assert_eq!(format!("{}", e), "tess: /nope: no such file");
63    }
64
65    #[test]
66    fn exit_code_for_startup_is_one() {
67        let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
68        let e = Error::OpenFile { path: "/nope".into(), source: io };
69        assert_eq!(e.exit_code(), 1);
70    }
71
72    #[test]
73    fn exit_code_for_runtime_is_two() {
74        let e = Error::Runtime("stdout closed".into());
75        assert_eq!(e.exit_code(), 2);
76    }
77}