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}
11
12impl Error {
13 pub fn exit_code(&self) -> i32 {
14 match self {
15 Error::OpenFile { .. } | Error::NotAFile { .. } | Error::NoInput => 1,
16 Error::Runtime(_) => 2,
17 }
18 }
19}
20
21impl fmt::Display for Error {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 Error::OpenFile { path, source } => {
25 write!(f, "tess: {}: {}", path.display(), source)
26 }
27 Error::NotAFile { path } => {
28 write!(f, "tess: {}: not a regular file", path.display())
29 }
30 Error::NoInput => write!(f, "tess: no input (specify a file or pipe stdin)"),
31 Error::Runtime(msg) => write!(f, "tess: {}", msg),
32 }
33 }
34}
35
36impl std::error::Error for Error {}
37
38pub type Result<T> = std::result::Result<T, Error>;
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn display_wraps_io_error_with_context() {
46 let io = std::io::Error::new(std::io::ErrorKind::NotFound, "no such file");
47 let e = Error::OpenFile { path: "/nope".into(), source: io };
48 assert_eq!(format!("{}", e), "tess: /nope: no such file");
49 }
50
51 #[test]
52 fn exit_code_for_startup_is_one() {
53 let io = std::io::Error::new(std::io::ErrorKind::NotFound, "x");
54 let e = Error::OpenFile { path: "/nope".into(), source: io };
55 assert_eq!(e.exit_code(), 1);
56 }
57
58 #[test]
59 fn exit_code_for_runtime_is_two() {
60 let e = Error::Runtime("stdout closed".into());
61 assert_eq!(e.exit_code(), 2);
62 }
63}