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
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::{fmt, io};

use requestty::ErrorKind;
use std::error::Error;
use std::num::ParseIntError;
use RunError::*;
use SearchResultWarning::*;

/*
Variants prefixed with "Clap" will be printed by Clap as such:
error: Invalid value for '<arg>': <YOUR MESSAGE>
 */

pub type Result<T> = std::result::Result<T, RunError>;

#[derive(Debug)]
pub enum RunError {
    ClapNotUsize,
    InvalidYearRange(ParseIntError),
    ClapInvalidFormat,
    NoSearchResults,
    Reqwest(reqwest::Error),
    InputUserHalted,
    InputIo(io::Error), // includes crossterm
    NoDesiredSearchResults,
    Serde(Box<dyn Error>),
    OmdbNotFound(String),                        // search term
    OmdbUnrecognised(String, serde_json::Error), // raw response JSON
}

impl RunError {
    pub fn error_code(&self) -> i32 {
        /*
        0 for success
        1 for user error
        2 for program error
         */
        match self {
            ClapNotUsize => 1,
            InvalidYearRange(_) => 1,
            ClapInvalidFormat => 1,
            NoSearchResults => 1,
            Reqwest(_) => 2,
            InputUserHalted => 1,
            InputIo(_) => 2,
            NoDesiredSearchResults => 0,
            Serde(_) => 2,
            OmdbNotFound(_) => 1,
            OmdbUnrecognised(_, _) => 2,
        }
    }
}

impl fmt::Display for RunError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ClapNotUsize => write!(f, "expected a positive integer"),
            InvalidYearRange(err) => write!(f, "Invalid year / year range ({})", err),
            ClapInvalidFormat => write!(
                f,
                "invalid format\nIf you think this should have \
            worked, please ensure you installed the tool with the required features\n\
            See the project README for more information"
            ),
            NoSearchResults => write!(f, "No search results"),
            Reqwest(reqwest_err) => write!(f, "Issue with web request: {}", reqwest_err),
            InputUserHalted => write!(f, "Program halted at user request"),
            InputIo(io_err) => write!(f, "IO error: {}", io_err),
            NoDesiredSearchResults => write!(f, "You couldn't find what you wanted :("),
            Serde(e) => write!(f, "Failed to serialise output data ({})", e),
            OmdbNotFound(search_term) => write!(f, "No record found on OMDb for {:?}", search_term),
            OmdbUnrecognised(json, err) => write!(
                f,
                "Unrecognised response from OMDb, please raise an issue including the following text:\n\
                Serde error: {}\n\
                JSON: {}",
                err, json
            ),
        }
    }
}

impl Error for RunError {}

impl From<reqwest::Error> for RunError {
    fn from(reqwest_err: reqwest::Error) -> Self {
        Reqwest(reqwest_err)
    }
}

impl From<requestty::ErrorKind> for RunError {
    fn from(requestty_err: ErrorKind) -> Self {
        use requestty::ErrorKind::*;
        match requestty_err {
            Eof | Interrupted => InputUserHalted,
            IoError(io_err) => Self::from(io_err),
        }
    }
}

impl From<io::Error> for RunError {
    fn from(io_err: io::Error) -> Self {
        InputIo(io_err)
    }
}

impl From<serde_json::Error> for RunError {
    fn from(ser_err: serde_json::Error) -> Self {
        Serde(Box::new(ser_err))
    }
}

#[cfg(feature = "yaml")]
impl From<serde_yaml::Error> for RunError {
    fn from(ser_err: serde_yaml::Error) -> Self {
        Serde(Box::new(ser_err))
    }
}

#[derive(Debug)]
pub enum SearchResultWarning {
    ImdbIdNotFound(String),
    NameNotFound(String),
}

impl fmt::Display for SearchResultWarning {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ImdbIdNotFound(s) => write!(f, "IMDb ID not found, please raise an issue if you are able to see the ID in the following text: {:?}", s),
            NameNotFound(s) => write!(f, "Movie/Show name not found, please raise an issue if you are able to see a name in the following text: {:?}", s),
        }
    }
}

impl Error for SearchResultWarning {}