diff options
Diffstat (limited to 'headers/src/error.rs')
-rw-r--r-- | headers/src/error.rs | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/headers/src/error.rs b/headers/src/error.rs new file mode 100644 index 0000000..beb92cb --- /dev/null +++ b/headers/src/error.rs @@ -0,0 +1,205 @@ +//! module contains the (new) errors emitted by this crate +use std::fmt::{self, Display}; + +use failure::{Fail, Context, Error as FError, Backtrace}; + +use ::name::HeaderName; + +/// This error can occur if different implementations for the +/// same header (e.g. `Subject`) where used in the same `HeaderMap`. +#[derive(Debug, Fail)] +#[fail(display = "cast error caused by mixing different header implementations for {}", header_name)] +pub struct HeaderTypeError { + header_name: HeaderName, + backtrace: Backtrace +} + +impl HeaderTypeError { + pub fn new(name: HeaderName) -> Self { + HeaderTypeError { + header_name: name, + backtrace: Backtrace::new() + } + } + + pub fn new_with_backtrace(name: HeaderName, backtrace: Backtrace) -> Self { + HeaderTypeError { + header_name: name, + backtrace + } + } +} + +/// A validator specified in a header definition failed. +/// +/// Common validators are e.g. to make sure that if a +/// From header has multiple mailboxes that there is +/// a Sender header etc. +#[derive(Debug, Fail)] +pub enum HeaderValidationError { + #[fail(display = "{}", _0)] + BuildIn(Context<BuildInValidationError>), + #[fail(display = "{}", _0)] + Custom(FError) +} + +impl From<BuildInValidationError> for HeaderValidationError { + fn from(err: BuildInValidationError) -> Self { + HeaderValidationError::BuildIn(Context::new(err)) + } +} + +impl From<Context<BuildInValidationError>> for HeaderValidationError { + fn from(err: Context<BuildInValidationError>) -> Self { + HeaderValidationError::BuildIn(err) + } +} + +/// The build-in error variants (error kinds) which can be returned +/// when running a header map validator. +#[derive(Copy, Clone, Debug, Fail, PartialEq, Eq, Hash)] +pub enum BuildInValidationError { + + /// This error is returned by `use_contextual_validators` if there is a "max one" inconsistency. + /// + /// I.e. if multiple implementations of the same header are used in the same map but + /// the implementations do not agree on wether or not the header can appear at most one + /// time in a header section. + #[fail(display = "{} header field contained both \"multi\" and \"max one\" header impl", header_name)] + MaxOneInconsistency { header_name: &'static str }, + + #[fail(display = "{} header field can appear at most one time in a header map", header_name)] + MoreThenOne { header_name: &'static str }, + + #[fail(display = "From field contained multiple addresses but no Sender field was set")] + MultiMailboxFromWithoutSender, + + #[fail(display = "each resent block must have a resent-date field")] + ResentDateFieldMissing, + + #[fail(display = "Resent-From field in resent block without a Resent-Sender field")] + MultiMailboxResentFromWithoutResentSender +} + +macro_rules! header_validation_bail { + (kind: $($tt:tt)*) => ({ + let build_in = $crate::error::BuildInValidationError::$($tt)*; + return Err(HeaderValidationError::BuildIn(::failure::Context::new(build_in))); + }); +} + + +/// Helper type which is either a `Backtrace` or an full `failure::Error`. +/// +/// This can be used to either just contain a backtrace into an custom +/// error or to chain it in front of another error without adding another +/// backtrace, depending on the creating context. +#[derive(Debug)] +pub enum ChainTail { + Backtrace(Backtrace), + Error(FError) +} + +impl ChainTail { + + fn backtrace(&self) -> &Backtrace { + match *self { + ChainTail::Backtrace(ref trace) => trace, + ChainTail::Error(ref error) => error.backtrace() + } + } + + fn as_fail(&self) -> Option<&Fail> { + match *self { + ChainTail::Backtrace(_) => None, + ChainTail::Error(ref error) => Some(error.as_fail()) + } + } +} + +/// Creating a (header field) component from the given data failed +/// +/// A good example converting a string to a mailbox by parsing it, +/// or more concretely failing to do so because it's not a valid +/// mail address. +#[derive(Debug)] +pub struct ComponentCreationError { + component: &'static str, + backtrace: ChainTail, + str_context: Option<String> +} + +impl ComponentCreationError { + + /// create a new `ComponentCreationError` based on a different error and the name of the component + /// + /// The name is normally the type name, for example `Email`, `Mailbox` etc. + pub fn from_parent<P>(parent: P, component: &'static str) -> Self + where P: Into<FError> + { + ComponentCreationError { + component, + backtrace: ChainTail::Error(parent.into()), + str_context: None + } + } + + /// creates a new `ComponentCreationError` based on the components name + /// + /// The name is normally the type name, for example `Email`, `Mailbox` etc. + pub fn new(component: &'static str) -> Self { + ComponentCreationError { + component, + backtrace: ChainTail::Backtrace(Backtrace::new()), + str_context: None + } + } + + /// creates a new `ComponentCreationError` based on the components name with a str_context + /// + /// The name is normally the type name, for example `Email`, `Mailbox` etc. + /// + /// The `str_context` is a snipped of text which can help a human to identify the + /// invalid parts, e.g. for parsing a email it could be the invalid email address. + pub fn new_with_str<I>(component: &'static str, str_context: I) -> Self + where I: Into<String> + { + ComponentCreationError { + component, + backtrace: ChainTail::Backtrace(Backtrace::new()), + str_context: Some(str_context.into()) + } + } + + pub fn str_context(&self) -> Option<&str> { + self.str_context.as_ref().map(|s|&**s) + } + + pub fn set_str_context<I>(&mut self, ctx: I) + where I: Into<String> + { + self.str_context = Some(ctx.into()); + } + + pub fn with_str_context<I>(mut self, ctx: I) -> Self + where I: Into<String> + { + self.set_str_context(ctx); + self + } +} + +impl Fail for ComponentCreationError { + fn cause(&self) -> Option<&Fail> { + self.backtrace.as_fail() + } + fn backtrace(&self) -> Option<&Backtrace> { + Some(self.backtrace.backtrace()) + } +} + +impl Display for ComponentCreationError { + fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result { + write!(fter, "creating component {} failed", self.component) + } +} |