1use std::fmt::{self, Display};
3
4use failure::{Context, Fail, Backtrace};
5use ::MailType;
6
7pub const UNKNOWN: &str = "<unknown>";
8pub const UTF_8: &str = "utf-8";
9pub const US_ASCII: &str = "us-ascii";
10
11#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
13pub enum EncodingErrorKind {
14
15 #[fail(display = "expected <{}> text encoding {} got ",
16 expected_encoding, got_encoding)]
17 InvalidTextEncoding {
18 expected_encoding: &'static str,
19 got_encoding: &'static str
21 },
22
23 #[fail(display = "hard line length limit breached (>= 998 bytes without CRLF)")]
24 HardLineLengthLimitBreached,
25
26 #[fail(display = "data can not be encoded with the {} encoding", encoding)]
27 NotEncodable {
28 encoding: &'static str,
29 },
30
31 #[fail(display = "malformed data")]
32 Malformed,
33
34 #[fail(display = "the mail body data cannot be accessed")]
35 AccessingMailBodyFailed,
36
37 #[fail(display = "{}", kind)]
38 Other { kind: &'static str }
39
40 }
43
44
45#[derive(Debug)]
53pub struct EncodingError {
54 inner: Context<EncodingErrorKind>,
55 mail_type: Option<MailType>,
56 str_context: Option<String>,
57 place: Option<Place>
58}
59
60#[derive(Debug)]
61pub enum Place {
62 Header { name: &'static str },
63 Body
64}
65
66impl EncodingError {
67 pub fn kind(&self) -> EncodingErrorKind {
69 *self.inner.get_context()
70 }
71
72 pub fn mail_type(&self) -> Option<MailType> {
74 self.mail_type
75 }
76
77 pub fn str_context(&self) -> Option<&str> {
79 self.str_context.as_ref().map(|s| &**s)
80 }
81
82 pub fn set_str_context<I>(&mut self, ctx: I)
84 where I: Into<String>
85 {
86 self.str_context = Some(ctx.into());
87 }
88
89 pub fn with_str_context<I>(mut self, ctx: I) -> Self
91 where I: Into<String>
92 {
93 self.set_str_context(ctx);
94 self
95 }
96
97 pub fn with_place_or_else<F>(mut self, func: F) -> Self
99 where F: FnOnce() -> Option<Place>
100 {
101 if self.place.is_none() {
102 self.place = func();
103 }
104 self
105 }
106
107 pub fn with_mail_type_or_else<F>(mut self, func: F) -> Self
109 where F: FnOnce() -> Option<MailType>
110 {
111 if self.mail_type.is_none() {
112 self.mail_type = func();
113 }
114 self
115 }
116}
117
118impl From<EncodingErrorKind> for EncodingError {
119 fn from(ctx: EncodingErrorKind) -> Self {
120 EncodingError::from(Context::new(ctx))
121 }
122}
123
124impl From<Context<EncodingErrorKind>> for EncodingError {
125 fn from(inner: Context<EncodingErrorKind>) -> Self {
126 EncodingError {
127 inner,
128 mail_type: None,
129 str_context: None,
130 place: None
131 }
132 }
133}
134
135impl From<(EncodingErrorKind, MailType)> for EncodingError {
136 fn from((ctx, mail_type): (EncodingErrorKind, MailType)) -> Self {
137 EncodingError::from((Context::new(ctx), mail_type))
138 }
139}
140
141impl From<(Context<EncodingErrorKind>, MailType)> for EncodingError {
142 fn from((inner, mail_type): (Context<EncodingErrorKind>, MailType)) -> Self {
143 EncodingError {
144 inner,
145 mail_type: Some(mail_type),
146 str_context: None,
147 place: None
148 }
149 }
150}
151
152impl Fail for EncodingError {
153
154 fn cause(&self) -> Option<&Fail> {
155 self.inner.cause()
156 }
157
158 fn backtrace(&self) -> Option<&Backtrace> {
159 self.inner.backtrace()
160 }
161}
162
163impl Display for EncodingError {
164
165 fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
166 if let Some(mail_type) = self.mail_type() {
167 write!(fter, "[{:?}]", mail_type)?;
168 } else {
169 write!(fter, "[<no_mail_type>]")?;
170 }
171 Display::fmt(&self.inner, fter)
172 }
173}
174
175#[macro_export]
205macro_rules! ec_bail {
206 (kind: $($tt:tt)*) => ({
207 return Err($crate::error::EncodingError::from(
208 $crate::error::EncodingErrorKind:: $($tt)*).into())
209 });
210 (mail_type: $mt:expr, kind: $($tt:tt)*) => ({
211 return Err($crate::error::EncodingError::from((
212 $crate::error::EncodingErrorKind:: $($tt)*,
213 $mt
214 )).into())
215 });
216}
217
218#[cfg(test)]
219mod test {
220
221 #[test]
222 fn bail_compiles_v1() {
223 let func = || -> Result<(), ::error::EncodingError> {
224 ec_bail!(kind: Other { kind: "test"});
225 #[allow(unreachable_code)] Ok(())
226 };
227 assert!((func)().is_err());
228 }
229
230 #[test]
231 fn bail_compiles_v2() {
232 fn mail_type() -> ::MailType { ::MailType::Internationalized }
233 let func = || -> Result<(), ::error::EncodingError> {
234 ec_bail!(mail_type: mail_type(), kind: Other { kind: "testicle" });
235 #[allow(unreachable_code)] Ok(())
236 };
237 assert!((func)().is_err());
238 }
239}