word_tally/
exit_code.rs

1//! Exit codes following Unix sysexits.h conventions.
2
3use std::{io, process};
4
5use clap::error::ErrorKind as ClapErrorKind;
6
7use crate::error::Error as WordTallyError;
8
9/// Exit codes following Unix sysexits.h convention
10#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
11#[repr(u8)]
12pub enum ExitCode {
13    /// Successful termination
14    Success = 0,
15    /// General failure
16    Failure = 1,
17    /// Command line usage error
18    UsageError = 64,
19    /// Data format error
20    DataFormat = 65,
21    /// Cannot open input
22    InputNotFound = 66,
23    /// Internal software error
24    InternalError = 70,
25    /// Cannot create output
26    OutputFailed = 73,
27    /// I/O error
28    IoError = 74,
29    /// Permission denied
30    PermissionDenied = 77,
31}
32
33impl From<&io::Error> for ExitCode {
34    fn from(err: &io::Error) -> Self {
35        match err.kind() {
36            io::ErrorKind::NotFound => Self::InputNotFound,
37            io::ErrorKind::PermissionDenied => Self::PermissionDenied,
38            io::ErrorKind::AlreadyExists => Self::OutputFailed,
39            _ => Self::IoError,
40        }
41    }
42}
43
44impl From<&clap::Error> for ExitCode {
45    fn from(err: &clap::Error) -> Self {
46        match err.kind() {
47            // Successful `--help` or `--version` display
48            ClapErrorKind::DisplayHelp | ClapErrorKind::DisplayVersion => Self::Success,
49            // Clap usage errors
50            _ => Self::UsageError,
51        }
52    }
53}
54
55impl From<&WordTallyError> for ExitCode {
56    fn from(err: &WordTallyError) -> Self {
57        match err {
58            WordTallyError::Usage(_)
59            | WordTallyError::StdinNotMappable
60            | WordTallyError::NotMappable { .. }
61            | WordTallyError::PathInvalid
62            | WordTallyError::BytesRequired
63            | WordTallyError::Config(_) => Self::UsageError,
64            WordTallyError::Utf8 { .. }
65            | WordTallyError::Pattern { .. }
66            | WordTallyError::Json(_)
67            | WordTallyError::Csv(_)
68            | WordTallyError::ChunkOverflow { .. }
69            | WordTallyError::BatchOverflow { .. } => Self::DataFormat,
70            WordTallyError::MutexPoisoned => Self::InternalError,
71            WordTallyError::Io { source, .. } => Self::from(source),
72        }
73    }
74}
75
76impl From<&anyhow::Error> for ExitCode {
77    fn from(err: &anyhow::Error) -> Self {
78        err.downcast_ref::<io::Error>()
79            .map(Self::from)
80            .or_else(|| err.downcast_ref::<clap::Error>().map(Self::from))
81            .or_else(|| err.downcast_ref::<WordTallyError>().map(Self::from))
82            .unwrap_or(Self::Failure)
83    }
84}
85
86impl From<ExitCode> for process::ExitCode {
87    fn from(code: ExitCode) -> Self {
88        Self::from(code as u8)
89    }
90}