diff options
Diffstat (limited to 'headers/src/header.rs')
-rw-r--r-- | headers/src/header.rs | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/headers/src/header.rs b/headers/src/header.rs new file mode 100644 index 0000000..ae63632 --- /dev/null +++ b/headers/src/header.rs @@ -0,0 +1,253 @@ +use std::any::TypeId; +use std::ops::{Deref, DerefMut}; +use std::fmt::{self, Debug}; + +use internals::{ + error::EncodingError, + encoder::{ + EncodableInHeader, + EncodingWriter, + } +}; + + +use ::error::ComponentCreationError; +use ::convert::HeaderTryInto; +use ::name::{HeaderName, HasHeaderName}; +//NOTE: this is a circular dependency between Header/HeaderMap +// but putting up e.g. a GenericHeaderMap trait/interface is +// not worth the work at all +use ::map::HeaderMapValidator; + +/// Trait representing a mail header. +/// +/// **This is not meant to be implemented by hand.*** +/// Use the `def_headers` macro instead. +/// +pub trait HeaderKind: Clone + Default + 'static { + + /// the component representing the header-field, e.g. `Unstructured` for `Subject` + type Component: EncodableInHeader + Clone; + + //FIXME[rust/const fn]: make this a associated constant + /// a method returning the header name + /// + /// # Note: + /// Once `const fn` is stable this will be changed to + /// a associated constant. + fn name() -> HeaderName; + + /// A function which is meant to be called with a reference + /// to the final header map before encoding the headers. It is + /// meant to be used do some of the contextual validations, + /// like e.g. a `From` header might return a function which + /// checks if the `From` header has multiple mailboxes and + /// if so checks if there is a `Sender` header + /// + /// Calling a contextual validator with a header map not + /// containing a header which it is meant to validate + /// should not cause an error. Only if the header is + /// there and the component is of the expected type + /// and it is invalid in the context + /// an error should be returned. + const VALIDATOR: Option<HeaderMapValidator>; + + /// I true this will assure that the header is at most one time in a header map. + /// + /// This is similar to `VALIDATOR` (and can be archived through one) but in difference + /// to any `VALIDATOR` this is already assured when inserting a header with MAX_ONE set + /// to true in a header map. It exists so that the header map can do, what is most + /// intuitive, replacing insertion for all `MAX_ONE` headers (like in a normal map) but + /// use adding insertion for all other header (like in a multi map). + /// + /// Most headers have this set to true. + const MAX_ONE: bool; + + /// Creates a `Header` instance automatically converting given body to the right type. + /// + /// # Error + /// + /// The type system assure that you can only use it on conversions + /// which are possible on type level, but they can still fail depending + /// on the actual data. For example creating a `Email` from a string + /// can fail if the string is not a valid email address. This in + /// turn means that creating a `From` header from a array of strings + /// can fail if one of them is not a valid email address. + fn auto_body<H>(body: H) -> Result<Header<Self>, ComponentCreationError> + where H: HeaderTryInto<Self::Component> + { + Ok(Self::body(HeaderTryInto::try_into(body)?)) + } + + /// Creates a `Header` instance for this header kind with given body. + fn body(body: Self::Component) -> Header<Self> { + Header::new(body) + } +} + +impl<H> HasHeaderName for H + where H: HeaderKind +{ + fn get_name(&self) -> HeaderName { + H::name() + } +} + +pub trait MaxOneMarker: HeaderKind {} + +#[derive(Clone)] +pub struct Header<H> + where H: HeaderKind +{ + body: H::Component +} + +impl<H> Header<H> + where H: HeaderKind +{ + pub fn new(body: H::Component) -> Header<H> { + Header { body } + } + + pub fn body(&self) -> &H::Component { + &self.body + } + + pub fn body_mut(&mut self) -> &mut H::Component { + &mut self.body + } +} + +impl<H> Deref for Header<H> + where H: HeaderKind +{ + type Target = H::Component; + fn deref(&self) -> &Self::Target { + self.body() + } +} + +impl<H> DerefMut for Header<H> + where H: HeaderKind +{ + fn deref_mut(&mut self) -> &mut Self::Target { + self.body_mut() + } +} + +impl<H> Debug for Header<H> + where H: HeaderKind +{ + fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result { + self.body.fmt(fter) + } +} + +/// Type alias for HeaderObjTrait's trait object. +pub type HeaderObj = dyn HeaderObjTrait; + +pub trait HeaderObjTrait: Sync + Send + ::std::any::Any + Debug { + fn name(&self) -> HeaderName; + fn is_max_one(&self) -> bool; + fn validator(&self) -> Option<HeaderMapValidator>; + fn encode(&self, encoder: &mut EncodingWriter) -> Result<(), EncodingError>; + fn boxed_clone(&self) -> Box<HeaderObj>; + + #[doc(hidden)] + fn type_id(&self) -> TypeId { + TypeId::of::<Self>() + } +} + +impl<H> HeaderObjTrait for Header<H> + where H: HeaderKind +{ + fn name(&self) -> HeaderName { + H::name() + } + + fn is_max_one(&self) -> bool { + H::MAX_ONE + } + + fn validator(&self) -> Option<HeaderMapValidator> { + H::VALIDATOR + } + + fn encode(&self, encoder: &mut EncodingWriter) -> Result<(), EncodingError> { + self.body.encode(encoder) + } + + fn boxed_clone(&self) -> Box<HeaderObj> { + let cloned = self.clone(); + Box::new(cloned) + } +} + +impl<H> HasHeaderName for Header<H> + where H: HeaderKind +{ + fn get_name(&self) -> HeaderName { + H::name() + } +} + + +impl HeaderObj { + pub fn is<H>(&self) -> bool + where H: HeaderKind + { + self.type_id() == TypeId::of::<Header<H>>() + } + + pub fn downcast_ref<H>(&self) -> Option<&Header<H>> + where H: HeaderKind + { + if self.is::<H>() { + Some(unsafe { &*(self as *const _ as *const Header<H>) }) + } else { + None + } + } + + pub fn downcast_mut<H>(&mut self) -> Option<&mut Header<H>> + where H: HeaderKind + { + if self.is::<H>() { + Some(unsafe { &mut *(self as *mut _ as *mut Header<H>) }) + } else { + None + } + } +} + +impl Clone for Box<HeaderObj> { + fn clone(&self) -> Self { + self.boxed_clone() + } +} + +impl HasHeaderName for HeaderObj { + fn get_name(&self) -> HeaderName { + self.name() + } +} + +pub trait HeaderObjTraitBoxExt: Sized { + fn downcast<H>(self) -> Result<Box<Header<H>>, Self> + where H: HeaderKind; +} + +impl HeaderObjTraitBoxExt for Box<HeaderObjTrait> { + + fn downcast<H>(self) -> Result<Box<Header<H>>, Self> + where H: HeaderKind + { + if HeaderObjTrait::is::<H>(&*self) { + let ptr: *mut (HeaderObj) = Box::into_raw(self); + Ok(unsafe { Box::from_raw(ptr as *mut Header<H>) }) + } else { + Err(self) + } + } +}
\ No newline at end of file |