1
/*
2
 * This file is part of mailpot
3
 *
4
 * Copyright 2020 - Manos Pitsidianakis
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as
8
 * published by the Free Software Foundation, either version 3 of the
9
 * License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18
 */
19

            
20
//! Errors of this library.
21

            
22
use std::sync::Arc;
23

            
24
use thiserror::Error;
25

            
26
/// Mailpot library error.
27
#[derive(Error, Debug)]
28
pub struct Error {
29
    kind: ErrorKind,
30
    source: Option<Arc<Self>>,
31
}
32

            
33
/// Mailpot library error.
34
37
#[derive(Error, Debug)]
35
pub enum ErrorKind {
36
    /// Post rejected.
37
    #[error("Your post has been rejected: {0}")]
38
    PostRejected(String),
39
    /// An entry was not found in the database.
40
6
    #[error("This {0} is not present in the database.")]
41
    NotFound(&'static str),
42
    /// A request was invalid.
43
    #[error("Your list request has been found invalid: {0}.")]
44
    InvalidRequest(String),
45
    /// An error happened and it was handled internally.
46
    #[error("An error happened and it was handled internally: {0}.")]
47
    Information(String),
48
    /// An error that shouldn't happen and should be reported.
49
    #[error("An error that shouldn't happen and should be reported: {0}.")]
50
    Bug(String),
51

            
52
    /// Error returned from an external user initiated operation such as
53
    /// deserialization or I/O.
54
1
    #[error("Error: {0}")]
55
    External(#[from] anyhow::Error),
56
    /// Generic
57
4
    #[error("{0}")]
58
    Generic(anyhow::Error),
59
    /// Error returned from sqlite3.
60
8
    #[error("Error returned from sqlite3: {0}.")]
61
    Sql(
62
        #[from]
63
        #[source]
64
        rusqlite::Error,
65
    ),
66
    /// Error returned from sqlite3.
67
    #[error("Error returned from sqlite3: {0}")]
68
    SqlLib(
69
        #[from]
70
        #[source]
71
        rusqlite::ffi::Error,
72
    ),
73
    /// Error returned from internal I/O operations.
74
1
    #[error("Error returned from internal I/O operation: {0}")]
75
    Io(#[from] ::std::io::Error),
76
    /// Error returned from e-mail protocol operations from `melib` crate.
77
    #[error("Error returned from e-mail protocol operations from `melib` crate: {0}")]
78
    Melib(#[from] melib::error::Error),
79
    /// Error from deserializing JSON values.
80
    #[error("Error from deserializing JSON values: {0}")]
81
    SerdeJson(#[from] serde_json::Error),
82
    /// Error returned from minijinja template engine.
83
    #[error("Error returned from minijinja template engine: {0}")]
84
    Template(#[from] minijinja::Error),
85
}
86

            
87
impl std::fmt::Display for Error {
88
4
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
89
4
        write!(fmt, "{}", self.kind)
90
4
    }
91
}
92

            
93
impl From<ErrorKind> for Error {
94
7
    fn from(kind: ErrorKind) -> Self {
95
7
        Self { kind, source: None }
96
7
    }
97
}
98

            
99
macro_rules! impl_from {
100
    ($ty:ty) => {
101
        impl From<$ty> for Error {
102
17
            fn from(err: $ty) -> Self {
103
17
                Self {
104
17
                    kind: err.into(),
105
17
                    source: None,
106
                }
107
17
            }
108
        }
109
    };
110
}
111

            
112
impl_from! { anyhow::Error }
113
impl_from! { rusqlite::Error }
114
impl_from! { rusqlite::ffi::Error }
115
impl_from! { ::std::io::Error }
116
impl_from! { melib::error::Error }
117
impl_from! { serde_json::Error }
118
impl_from! { minijinja::Error }
119

            
120
impl Error {
121
    /// Helper function to create a new generic error message.
122
    pub fn new_external<S: Into<String>>(msg: S) -> Self {
123
        let msg = msg.into();
124
        ErrorKind::External(anyhow::Error::msg(msg)).into()
125
    }
126

            
127
    /// Chain an error by introducing a new head of the error chain.
128
3
    pub fn chain_err<E>(self, lambda: impl FnOnce() -> E) -> Self
129
    where
130
        E: Into<Self>,
131
    {
132
3
        let new_head: Self = lambda().into();
133
3
        Self {
134
3
            source: Some(Arc::new(self)),
135
            ..new_head
136
        }
137
3
    }
138

            
139
    /// Insert a source error into this Error.
140
    pub fn with_source<E>(self, source: E) -> Self
141
    where
142
        E: Into<Self>,
143
    {
144
        Self {
145
            source: Some(Arc::new(source.into())),
146
            ..self
147
        }
148
    }
149

            
150
    /// Getter for the kind field.
151
4
    pub fn kind(&self) -> &ErrorKind {
152
        &self.kind
153
4
    }
154

            
155
    /// Display error chain to user.
156
2
    pub fn display_chain(&'_ self) -> impl std::fmt::Display + '_ {
157
2
        ErrorChainDisplay {
158
            current: self,
159
            counter: 1,
160
        }
161
4
    }
162
}
163

            
164
impl From<String> for Error {
165
3
    fn from(s: String) -> Self {
166
3
        ErrorKind::Generic(anyhow::Error::msg(s)).into()
167
3
    }
168
}
169
impl From<&str> for Error {
170
1
    fn from(s: &str) -> Self {
171
1
        ErrorKind::Generic(anyhow::Error::msg(s.to_string())).into()
172
1
    }
173
}
174

            
175
/// Type alias for Mailpot library Results.
176
pub type Result<T> = std::result::Result<T, Error>;
177

            
178
struct ErrorChainDisplay<'e> {
179
    current: &'e Error,
180
    counter: usize,
181
}
182

            
183
impl std::fmt::Display for ErrorChainDisplay<'_> {
184
5
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
185
5
        if let Some(ref source) = self.current.source {
186
3
            writeln!(fmt, "[{}] {} Caused by:", self.counter, self.current.kind)?;
187
3
            Self {
188
3
                current: source,
189
3
                counter: self.counter + 1,
190
            }
191
            .fmt(fmt)
192
        } else {
193
5
            writeln!(fmt, "[{}] {}", self.counter, self.current.kind)?;
194
2
            Ok(())
195
        }
196
7
    }
197
}
198

            
199
/// adfsa
200
pub trait Context<T> {
201
    /// Wrap the error value with additional context.
202
    fn context<C>(self, context: C) -> Result<T>
203
    where
204
        C: Into<Error>;
205

            
206
    /// Wrap the error value with additional context that is evaluated lazily
207
    /// only once an error does occur.
208
    fn with_context<C, F>(self, f: F) -> Result<T>
209
    where
210
        C: Into<Error>,
211
        F: FnOnce() -> C;
212
}
213

            
214
impl<T, E> Context<T> for std::result::Result<T, E>
215
where
216
    Error: From<E>,
217
{
218
99
    fn context<C>(self, context: C) -> Result<T>
219
    where
220
        C: Into<Error>,
221
    {
222
99
        self.map_err(|err| Error::from(err).chain_err(|| context.into()))
223
99
    }
224

            
225
207
    fn with_context<C, F>(self, f: F) -> Result<T>
226
    where
227
        C: Into<Error>,
228
        F: FnOnce() -> C,
229
    {
230
209
        self.map_err(|err| Error::from(err).chain_err(|| f().into()))
231
207
    }
232
}