mail_core/
error.rs

1//! Module containing all custom errors produced by this crate.
2use std::fmt::{self, Display};
3use std::io;
4
5use failure::{Fail, Context, Backtrace};
6
7use internals::error::EncodingError;
8use headers::error::{
9    BuildInValidationError,
10    HeaderTypeError, ComponentCreationError,
11    HeaderValidationError
12};
13use ::IRI;
14// errors from loading a Resource (which includes encoding it's body)
15//                /  NotFound       | IRI (no Backtrace neede)     \ MailError::ResourceLoading
16// ResourceError <   LoadingFailed  | chain Error                  /
17//                \  EncodingFailed | EncodingError (Backtrace!)   > MailError::Encoding
18//
19
20/// Error caused by failing to load an `Resource`
21///
22#[derive(Debug, Fail)]
23pub enum ResourceError {
24    /// The loading on itself failed.
25    #[fail(display = "{}", _0)]
26    Loading(ResourceLoadingError),
27
28    /// The encoding of the resource failed.
29    ///
30    /// Note: Resources are encoded as this allows shared
31    /// resources to not be re-encoded every time they are
32    /// used.
33    #[fail(display = "{}", _0)]
34    Encoding(EncodingError)
35}
36
37impl From<EncodingError> for ResourceError {
38    fn from(err: EncodingError) -> Self {
39        ResourceError::Encoding(err)
40    }
41}
42
43impl From<ResourceLoadingError> for ResourceError {
44    fn from(err: ResourceLoadingError) -> Self {
45        ResourceError::Loading(err)
46    }
47}
48
49/// Reasons why the loading of an `Resource` can fail.
50#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
51pub enum ResourceLoadingErrorKind {
52    /// The resource wasn't found.
53    #[fail(display = "resource not found")]
54    NotFound,
55
56    /// The act of loading it failed (e.g. because of an I/0-Error)
57    #[fail(display = "loading failed")]
58    LoadingFailed,
59
60    #[fail(display = "automatically detecting the media type failed")]
61    MediaTypeDetectionFailed
62}
63
64/// The loading of an Resource failed.
65#[derive(Debug)]
66pub struct ResourceLoadingError {
67    inner: Context<ResourceLoadingErrorKind>,
68    iri: Option<IRI>
69}
70
71impl Display for ResourceLoadingError {
72    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
73        Display::fmt(&self.inner, fter)
74    }
75}
76
77impl Fail for ResourceLoadingError {
78    fn cause(&self) -> Option<&Fail> {
79        self.inner.cause()
80    }
81
82    fn backtrace(&self) -> Option<&Backtrace> {
83        self.inner.backtrace()
84    }
85}
86
87impl ResourceLoadingError {
88
89    /// The kind of error which caused the loading to fail.
90    pub fn kind(&self) -> ResourceLoadingErrorKind {
91        *self.inner.get_context()
92    }
93
94    /// The source IRI which was used when failing to load the Resource.
95    pub fn source_iri(&self) -> Option<&IRI> {
96        self.iri.as_ref()
97    }
98
99    /// Sets the source IRI if not already set and returns self.
100    pub fn with_source_iri_or_else<F>(mut self, func: F) -> Self
101        where F: FnOnce() -> Option<IRI>
102    {
103        if self.iri.is_none() {
104            self.iri = func();
105        }
106        self
107    }
108}
109
110impl From<ResourceLoadingErrorKind> for ResourceLoadingError {
111    fn from(err: ResourceLoadingErrorKind) -> Self {
112        ResourceLoadingError::from((None, err))
113    }
114}
115
116impl From<Context<ResourceLoadingErrorKind>> for ResourceLoadingError {
117    fn from(inner: Context<ResourceLoadingErrorKind>) -> Self {
118        ResourceLoadingError::from((None, inner))
119    }
120}
121
122impl From<(IRI, ResourceLoadingErrorKind)> for ResourceLoadingError {
123    fn from((iri, error_kind): (IRI, ResourceLoadingErrorKind)) -> Self {
124        ResourceLoadingError::from((Some(iri), error_kind))
125    }
126}
127
128impl From<(IRI, Context<ResourceLoadingErrorKind>)> for ResourceLoadingError {
129    fn from((iri, inner): (IRI, Context<ResourceLoadingErrorKind>)) -> Self {
130        ResourceLoadingError::from((Some(iri), inner))
131    }
132}
133
134impl From<(Option<IRI>, ResourceLoadingErrorKind)> for ResourceLoadingError {
135    fn from((iri, error_kind): (Option<IRI>, ResourceLoadingErrorKind)) -> Self {
136        ResourceLoadingError::from((iri, Context::new(error_kind)))
137    }
138}
139
140impl From<(Option<IRI>, Context<ResourceLoadingErrorKind>)> for ResourceLoadingError {
141    fn from((iri, inner): (Option<IRI>, Context<ResourceLoadingErrorKind>)) -> Self {
142        ResourceLoadingError {
143            inner, iri
144        }
145    }
146}
147
148impl From<io::Error> for ResourceLoadingError {
149    fn from(err: io::Error) -> Self {
150        err.context(ResourceLoadingErrorKind::LoadingFailed).into()
151    }
152}
153
154
155#[derive(Debug, Fail)]
156pub enum OtherValidationError {
157    /// Non-multipart mail headers derive the Content-Type header from it's body `Resource`.
158    ///
159    /// This error is returned if a `Content-Type` header was given never the less.
160    #[fail(display = "Content-Type header given for non multipart mail")]
161    ContentTypeHeaderGiven,
162
163    /// `Content-Transfer-Encoding` headers are always auto-generated
164    /// and can not be manually set.
165    #[fail(display = "Content-Transfer-Encoding header given")]
166    ContentTransferEncodingHeaderGiven,
167
168    /// A non "multipart" media type was given as content type for a multipart mail.
169    #[fail(display = "found non multipart content type in multipart mail")]
170    SingleMultipartMixup,
171
172    /// Inserting a `Conent-Type` header into a singlepart body is not allowed.
173    ///
174    /// In single-part bodies the `Content-Type` header is always auto-generated
175    /// based on the actual body.
176    #[fail(display = "inserting Content-Type for singlepart body is not allowed")]
177    InsertSinglepartContentTypeHeader,
178
179    /// A multipart mail requires a `Content-Type` header to be given.
180    #[fail(display = "multipart mail does not contain a content type header")]
181    MissingContentTypeHeader,
182
183    /// A mail (top level, not in multipart) requires a `From` header to be given.
184    #[fail(display = "mail did not contain a From header")]
185    NoFrom
186}
187
188impl From<OtherValidationError> for HeaderValidationError {
189    fn from(oe: OtherValidationError) -> Self {
190        let err: ::failure::Error = oe.into();
191        HeaderValidationError::Custom(err)
192    }
193}
194
195impl From<OtherValidationError> for MailError {
196    fn from(oe: OtherValidationError) -> Self {
197        let val_err = HeaderValidationError::from(oe);
198        MailError::from(val_err)
199    }
200}
201
202/// General Error combining most other error wrt. mail creation and encoding.
203#[derive(Debug, Fail)]
204pub enum MailError {
205    /// Encoding the mail failed.
206    #[fail(display = "{}", _0)]
207    Encoding(EncodingError),
208
209    /// Different implementations for the same header where mixed up.
210    #[fail(display = "{}", _0)]
211    Type(HeaderTypeError),
212
213    /// Creating a mail header body (component) failed.
214    #[fail(display = "{}", _0)]
215    Component(ComponentCreationError),
216
217    /// The mail has some invalid header or header combinations.
218    ///
219    /// E.g. it has a `From` header with multiple mailboxes but no
220    /// `Sender` header (which is only required if `From` has more
221    /// than one mailbox).
222    #[fail(display = "{}", _0)]
223    Validation(HeaderValidationError),
224
225    /// Loading an resource failed.
226    ///
227    /// E.g. the file to attach or the image to embedded could not
228    /// be found.
229    #[fail(display = "{}", _0)]
230    ResourceLoading(ResourceLoadingError)
231}
232
233impl From<BuildInValidationError> for MailError {
234    fn from(err: BuildInValidationError) -> Self {
235        MailError::Validation(err.into())
236    }
237}
238
239impl From<HeaderTypeError> for MailError {
240    fn from(err: HeaderTypeError) -> Self {
241        MailError::Type(err)
242    }
243}
244
245impl From<EncodingError> for MailError {
246    fn from(err: EncodingError) -> Self {
247        MailError::Encoding(err)
248    }
249}
250
251
252impl From<HeaderValidationError> for MailError {
253    fn from(err: HeaderValidationError) -> Self {
254        MailError::Validation(err)
255    }
256}
257
258impl From<ResourceLoadingError> for MailError {
259    fn from(err: ResourceLoadingError) -> Self {
260        MailError::ResourceLoading(err)
261    }
262}
263
264impl From<ResourceError> for MailError {
265    fn from(err: ResourceError) -> Self {
266        match err {
267            ResourceError::Loading(err) => MailError::ResourceLoading(err),
268            ResourceError::Encoding(err) => MailError::Encoding(err)
269        }
270    }
271}
272
273impl From<ComponentCreationError> for MailError {
274    fn from(err: ComponentCreationError) -> Self {
275        MailError::Component(err)
276    }
277}
278
279
280/// Error returned when trying to _unload_ and `Resource` and it fails.
281#[derive(Copy, Clone, Debug, Fail)]
282pub enum ResourceNotUnloadableError {
283    /// The resource can not be unloaded because its in use.
284    #[fail(display = "resource is in use, can't unload it")]
285    InUse,
286    /// The resource can not be unloaded because it doesn't has a source.
287    ///
288    /// Which means if we would unload it we could not reload it. Note
289    /// that unloading is just for thinks like caching, it doesn't affect
290    /// the deletion/dropping of `Resource` instances.
291    #[fail(display = "resource has no source, can't unload it")]
292    NoSource
293}