hyper_scripter/
error.rs

1use std::path::PathBuf;
2use std::sync::Arc;
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
5pub struct ExitCode(i32);
6pub const EXIT_OK: ExitCode = ExitCode(0);
7pub const EXIT_KNOWN_ERR: ExitCode = ExitCode(1);
8pub const EXIT_OTHER_ERR: ExitCode = ExitCode(2);
9impl ExitCode {
10    /// 將另一個 `ExitCode` 和自身比較,若對方較嚴重,則將自身的值變成對方
11    ///
12    /// ```
13    /// use hyper_scripter::error::*;
14    /// let mut code = EXIT_KNOWN_ERR;
15    /// code.cmp_and_replace(EXIT_OK);
16    /// assert_eq!(code, EXIT_KNOWN_ERR);
17    /// code.cmp_and_replace(EXIT_OTHER_ERR);
18    /// assert_eq!(code, EXIT_OTHER_ERR);
19    /// ```
20    pub fn cmp_and_replace(&mut self, code: ExitCode) {
21        self.0 = std::cmp::max(self.0, code.0);
22    }
23    pub fn code(&self) -> i32 {
24        self.0
25    }
26}
27
28#[derive(Debug, Clone, Copy, Eq, PartialEq)]
29pub enum SysPath {
30    Config,
31    Home,
32}
33#[derive(Debug, Clone, Copy, Eq, PartialEq)]
34pub enum FormatCode {
35    PromptLevel,
36    Config,
37    ScriptName,
38    ScriptType,
39    Regex,
40    RangeQuery,
41    ScriptQuery,
42    Tag,
43    NonEmptyArray,
44    EnvPair,
45    Template, // TODO: 一旦特化穩定了,就讓 handlebars 錯誤自動轉成這個
46}
47
48impl FormatCode {
49    pub fn to_err(&self, s: String) -> Error {
50        Error::Format(*self, s)
51    }
52    pub fn to_res<T>(&self, s: String) -> Result<T> {
53        Err(Error::Format(*self, s))
54    }
55    pub fn to_display_res<T>(&self, s: String) -> DisplayResult<T> {
56        Err(Error::Format(*self, s).into())
57    }
58}
59
60#[derive(Debug, Clone)]
61pub enum Error {
62    Others(
63        Vec<String>,
64        Option<Arc<dyn 'static + Send + Sync + std::error::Error>>,
65    ),
66    SysPathNotFound(SysPath),
67    EmptyCreate,
68
69    PermissionDenied(Vec<PathBuf>),
70    // NOTE: PathNotFound 比 ScriptNotFound 更嚴重,代表歷史記錄中有這支腳本,實際要找卻找不到
71    PathNotFound(Vec<PathBuf>),
72    GeneralFS(Vec<PathBuf>, Arc<std::io::Error>),
73
74    PathExist(PathBuf),
75    ScriptExist(String),
76    ScriptIsFiltered(String),
77    ScriptNotFound(String),
78    NoAlias(String),
79    UnknownType(String),
80    Format(FormatCode, String),
81
82    ScriptError(i32),
83    PreRunError(i32),
84    EditorError(i32, Vec<String>),
85
86    RedundantOpt(RedundantOpt),
87    TagSelectorNotFound(String),
88    DontFuzz,
89    NoPreviousArgs,
90    Empty,
91    Caution,
92
93    Completion,
94}
95
96impl<T: 'static + Send + Sync + std::error::Error> From<T> for Error {
97    fn from(t: T) -> Self {
98        Error::Others(vec![], Some(Arc::new(t)))
99    }
100}
101impl Error {
102    pub fn msg<T: ToString>(s: T) -> Self {
103        Error::Others(vec![s.to_string()], None)
104    }
105    pub fn context<T: ToString>(mut self, s: T) -> Self {
106        log::debug!("附註:{:?} + {}", self, s.to_string());
107        if let Error::Others(msg, ..) = &mut self {
108            msg.push(s.to_string());
109        }
110        self
111    }
112    pub fn code(&self) -> ExitCode {
113        use Error::*;
114        match self {
115            Others(..) | GeneralFS(..) => EXIT_OTHER_ERR,
116            ScriptError(c) | PreRunError(c) | EditorError(c, _) => ExitCode(*c),
117            _ => EXIT_KNOWN_ERR,
118        }
119    }
120}
121
122pub type Result<T = ()> = std::result::Result<T, Error>;
123
124pub trait Contextable<T> {
125    fn context<S: ToString>(self, s: S) -> Result<T>;
126}
127impl<T> Contextable<T> for Result<T> {
128    fn context<S: ToString>(self, s: S) -> Result<T> {
129        match self {
130            Ok(t) => Ok(t),
131            Err(e) => Err(e.context(s)),
132        }
133    }
134}
135
136impl<T, E: 'static + Send + Sync + std::error::Error> Contextable<T> for std::result::Result<T, E> {
137    fn context<S: ToString>(self, s: S) -> Result<T> {
138        match self {
139            Ok(t) => Ok(t),
140            Err(e) => {
141                let e: Error = e.into();
142                Err(e.context(s))
143            }
144        }
145    }
146}
147#[derive(Debug, Clone)]
148pub enum RedundantOpt {
149    Scripts(Vec<String>),
150    Tag,
151    Type,
152    Selector,
153}
154
155impl From<RedundantOpt> for Error {
156    fn from(opt: RedundantOpt) -> Self {
157        Error::RedundantOpt(opt)
158    }
159}
160
161// TODO: 一旦 specialization 穩了就直接把 StdError 實作在我們的錯誤結構上
162#[derive(Display, Debug)]
163pub struct DisplayError(Error);
164impl From<Error> for DisplayError {
165    fn from(err: Error) -> Self {
166        DisplayError(err)
167    }
168}
169impl DisplayError {
170    pub fn into_err(self) -> Error {
171        self.0
172    }
173}
174impl std::error::Error for DisplayError {}
175pub type DisplayResult<T = ()> = std::result::Result<T, DisplayError>;