summaryrefslogtreecommitdiffstats
path: root/internals/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'internals/src/error.rs')
-rw-r--r--internals/src/error.rs239
1 files changed, 239 insertions, 0 deletions
diff --git a/internals/src/error.rs b/internals/src/error.rs
new file mode 100644
index 0000000..eb13654
--- /dev/null
+++ b/internals/src/error.rs
@@ -0,0 +1,239 @@
+//! Module containing the `EncodingError`.
+use std::fmt::{self, Display};
+
+use failure::{Context, Fail, Backtrace};
+use ::MailType;
+
+pub const UNKNOWN: &str = "<unknown>";
+pub const UTF_8: &str = "utf-8";
+pub const US_ASCII: &str = "us-ascii";
+
+/// A general error appearing when encoding failed in some way.
+#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)]
+pub enum EncodingErrorKind {
+
+ #[fail(display = "expected <{}> text encoding {} got ",
+ expected_encoding, got_encoding)]
+ InvalidTextEncoding {
+ expected_encoding: &'static str,
+ //TODO[failure >= 0.2] make it Optional remove `UNKNOWN`
+ got_encoding: &'static str
+ },
+
+ #[fail(display = "hard line length limit breached (>= 998 bytes without CRLF)")]
+ HardLineLengthLimitBreached,
+
+ #[fail(display = "data can not be encoded with the {} encoding", encoding)]
+ NotEncodable {
+ encoding: &'static str,
+ },
+
+ #[fail(display = "malformed data")]
+ Malformed,
+
+ #[fail(display = "the mail body data cannot be accessed")]
+ AccessingMailBodyFailed,
+
+ #[fail(display = "{}", kind)]
+ Other { kind: &'static str }
+
+ //ErrorKinds potentially needed when using this wrt. to decoding the mail encoding
+ //UnsupportedEncoding { encoding: &'static str }
+}
+
+
+/// A general error appearing when encoding failed in some way.
+///
+/// This error consists of an `EncodingErrorKind` and a bit
+/// of contextual information including: The place the error
+/// happened in (`Header { name }`,`Body`), a string representing
+/// the context when it happens (e.g. the word which could not be encoded),
+/// and the mail type.
+#[derive(Debug)]
+pub struct EncodingError {
+ inner: Context<EncodingErrorKind>,
+ mail_type: Option<MailType>,
+ str_context: Option<String>,
+ place: Option<Place>
+}
+
+#[derive(Debug)]
+pub enum Place {
+ Header { name: &'static str },
+ Body
+}
+
+impl EncodingError {
+ /// Return the error kind.
+ pub fn kind(&self) -> EncodingErrorKind {
+ *self.inner.get_context()
+ }
+
+ /// Return the mail type used when the error appeared.
+ pub fn mail_type(&self) -> Option<MailType> {
+ self.mail_type
+ }
+
+ /// Returns the str_context associated with the error.
+ pub fn str_context(&self) -> Option<&str> {
+ self.str_context.as_ref().map(|s| &**s)
+ }
+
+ /// Sets the str context.
+ pub fn set_str_context<I>(&mut self, ctx: I)
+ where I: Into<String>
+ {
+ self.str_context = Some(ctx.into());
+ }
+
+ /// Returns a version of self which has a str context like the given one.
+ pub fn with_str_context<I>(mut self, ctx: I) -> Self
+ where I: Into<String>
+ {
+ self.set_str_context(ctx);
+ self
+ }
+
+ /// Adds a place (context) to self if there isn't one and returns self.
+ pub fn with_place_or_else<F>(mut self, func: F) -> Self
+ where F: FnOnce() -> Option<Place>
+ {
+ if self.place.is_none() {
+ self.place = func();
+ }
+ self
+ }
+
+ /// Adds a mail type (context) to self if there isn't one and returns self.
+ pub fn with_mail_type_or_else<F>(mut self, func: F) -> Self
+ where F: FnOnce() -> Option<MailType>
+ {
+ if self.mail_type.is_none() {
+ self.mail_type = func();
+ }
+ self
+ }
+}
+
+impl From<EncodingErrorKind> for EncodingError {
+ fn from(ctx: EncodingErrorKind) -> Self {
+ EncodingError::from(Context::new(ctx))
+ }
+}
+
+impl From<Context<EncodingErrorKind>> for EncodingError {
+ fn from(inner: Context<EncodingErrorKind>) -> Self {
+ EncodingError {
+ inner,
+ mail_type: None,
+ str_context: None,
+ place: None
+ }
+ }
+}
+
+impl From<(EncodingErrorKind, MailType)> for EncodingError {
+ fn from((ctx, mail_type): (EncodingErrorKind, MailType)) -> Self {
+ EncodingError::from((Context::new(ctx), mail_type))
+ }
+}
+
+impl From<(Context<EncodingErrorKind>, MailType)> for EncodingError {
+ fn from((inner, mail_type): (Context<EncodingErrorKind>, MailType)) -> Self {
+ EncodingError {
+ inner,
+ mail_type: Some(mail_type),
+ str_context: None,
+ place: None
+ }
+ }
+}
+
+impl Fail for EncodingError {
+
+ fn cause(&self) -> Option<&Fail> {
+ self.inner.cause()
+ }
+
+ fn backtrace(&self) -> Option<&Backtrace> {
+ self.inner.backtrace()
+ }
+}
+
+impl Display for EncodingError {
+
+ fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(mail_type) = self.mail_type() {
+ write!(fter, "[{:?}]", mail_type)?;
+ } else {
+ write!(fter, "[<no_mail_type>]")?;
+ }
+ Display::fmt(&self.inner, fter)
+ }
+}
+
+/// Macro for easier returning an `EncodingError`.
+///
+/// It will use the given input to create and
+/// `EncodingError` _and return it_ (like `try!`/`?`
+/// returns an error, i.e. it places a return statement
+/// in the code).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate mail_internals;
+/// # use mail_internals::{MailType, error::EncodingError};
+/// # fn main() {
+/// # fn failed_something() -> bool { true }
+/// # let func = || -> Result<(), EncodingError> {
+/// if failed_something() {
+/// // Note that the `mail_type: ...` part is not required and
+/// // `EncodingErrorKind` does _not_ need to be imported.
+/// ec_bail!(mail_type: MailType::Internationalized, kind: InvalidTextEncoding {
+/// expected_encoding: "utf-8",
+/// got_encoding: "utf-32"
+/// });
+/// }
+/// # Ok(())
+/// # };
+/// #
+/// # }
+/// ```
+///
+#[macro_export]
+macro_rules! ec_bail {
+ (kind: $($tt:tt)*) => ({
+ return Err($crate::error::EncodingError::from(
+ $crate::error::EncodingErrorKind:: $($tt)*).into())
+ });
+ (mail_type: $mt:expr, kind: $($tt:tt)*) => ({
+ return Err($crate::error::EncodingError::from((
+ $crate::error::EncodingErrorKind:: $($tt)*,
+ $mt
+ )).into())
+ });
+}
+
+#[cfg(test)]
+mod test {
+
+ #[test]
+ fn bail_compiles_v1() {
+ let func = || -> Result<(), ::error::EncodingError> {
+ ec_bail!(kind: Other { kind: "test"});
+ #[allow(unreachable_code)] Ok(())
+ };
+ assert!((func)().is_err());
+ }
+
+ #[test]
+ fn bail_compiles_v2() {
+ fn mail_type() -> ::MailType { ::MailType::Internationalized }
+ let func = || -> Result<(), ::error::EncodingError> {
+ ec_bail!(mail_type: mail_type(), kind: Other { kind: "testicle" });
+ #[allow(unreachable_code)] Ok(())
+ };
+ assert!((func)().is_err());
+ }
+} \ No newline at end of file