diff options
Diffstat (limited to 'headers/src/header_components/mailbox_list.rs')
-rw-r--r-- | headers/src/header_components/mailbox_list.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/headers/src/header_components/mailbox_list.rs b/headers/src/header_components/mailbox_list.rs new file mode 100644 index 0000000..68a9a30 --- /dev/null +++ b/headers/src/header_components/mailbox_list.rs @@ -0,0 +1,300 @@ +use std::iter::IntoIterator; +use vec1::Vec1; +use soft_ascii_string::SoftAsciiChar; + +use internals::error::EncodingError; +use internals::encoder::{EncodableInHeader, EncodingWriter}; +use ::{ HeaderTryFrom, HeaderTryInto}; +use ::error::ComponentCreationError; + +use super::Mailbox; + +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub struct OptMailboxList( pub Vec<Mailbox> ); + +#[derive(Debug, Hash, Eq, PartialEq, Clone)] +pub struct MailboxList( pub Vec1<Mailbox> ); + +impl MailboxList { + pub fn from_single( m: Mailbox ) -> Self { + MailboxList( Vec1::new( m ) ) + } +} + +impl IntoIterator for MailboxList { + type Item = <Vec1<Mailbox> as IntoIterator>::Item; + type IntoIter = <Vec1<Mailbox> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + + + +impl EncodableInHeader for OptMailboxList { + + fn encode(&self, handle: &mut EncodingWriter) -> Result<(), EncodingError> { + encode_list( self.0.iter(), handle ) + } + + fn boxed_clone(&self) -> Box<EncodableInHeader> { + Box::new(self.clone()) + } +} + +//impl HeaderTryFrom<Mailbox> for OptMailboxList { +// fn try_from( mbox: Mailbox ) -> Result<Self> { +// Ok( OptMailboxList( vec![ mbox ] ) ) +// } +//} + +//impl<T> HeaderTryFrom<T> for MailboxList +// where T: HeaderTryInto<Mailbox> +//{ +// fn try_from( mbox: T ) -> Result<Self> { +// let mbox = mbox.try_into()?; +// Ok( MailboxList( Vec1::new( mbox ) ) ) +// } +//} + +//TODO-RUST-RFC: allow conflicting wildcard implementations if priority is specified +// if done then we can implement it for IntoIterator instead of Vec and slice +impl<T> HeaderTryFrom<Vec<T>> for MailboxList + where T: HeaderTryInto<Mailbox> +{ + fn try_from(vec: Vec<T>) -> Result<Self, ComponentCreationError> { + try_from_into_iter( vec ) + } +} + +fn try_from_into_iter<IT>( mboxes: IT ) -> Result<MailboxList, ComponentCreationError> + where IT: IntoIterator, IT::Item: HeaderTryInto<Mailbox> +{ + let mut iter = mboxes.into_iter(); + let mut vec = if let Some( first) = iter.next() { + Vec1::new( first.try_into()? ) + } else { + //TODO chain vec1 Size0Error + return Err(ComponentCreationError::new("MailboxList")); + }; + for mbox in iter { + vec.push( mbox.try_into()? ); + } + Ok( MailboxList( vec ) ) +} + +macro_rules! impl_header_try_from_array { + (_MBoxList 0) => (); + (_MBoxList $len:tt) => ( + impl<T> HeaderTryFrom<[T; $len]> for MailboxList + where T: HeaderTryInto<Mailbox> + { + fn try_from( vec: [T; $len] ) -> Result<Self, ComponentCreationError> { + //due to only supporting arrays halfheartedly for now + let heapified: Box<[T]> = Box::new(vec); + let vecified: Vec<_> = heapified.into(); + try_from_into_iter( vecified ) + } + } + ); + (_OptMBoxList $len:tt) => ( + impl<T> HeaderTryFrom<[T; $len]> for OptMailboxList + where T: HeaderTryInto<Mailbox> + { + fn try_from( vec: [T; $len] ) -> Result<Self, ComponentCreationError> { + let heapified: Box<[T]> = Box::new(vec); + let vecified: Vec<_> = heapified.into(); + let mut out = Vec::new(); + for ele in vecified.into_iter() { + out.push( ele.try_into()? ); + } + Ok( OptMailboxList( out ) ) + } + } + ); + ($($len:tt)*) => ($( + impl_header_try_from_array!{ _MBoxList $len } + impl_header_try_from_array!{ _OptMBoxList $len } + )*); +} + +impl_header_try_from_array! { + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 +} + +//TODO also implement for phrase list +macro_rules! impl_header_try_from_tuple { + (_MBoxList []) => ( + compiler_error!("mailbox list needs at last one element") + ); + (_MBoxList [ $($vs:ident),* ]) => ( + impl< $($vs),* > HeaderTryFrom<( $($vs,)* )> for MailboxList + where $($vs: HeaderTryInto<Mailbox>),* + { + #[allow(non_snake_case)] + fn try_from( ($($vs,)*): ($($vs,)*) ) -> Result<Self, ComponentCreationError> { + // we use the type names as variable names, + // not nice but it works + //let ($($vs),*) = src; + let mut out = Vec::new(); + $( + let $vs = $vs.try_into()?; + out.push($vs); + )* + Ok( MailboxList( + //UNWRAP_SAFE: len 0 is not implemented with the macro + $crate::vec1::Vec1::from_vec(out).unwrap() + ) ) + } + } + ); + (_OptMBoxList [$($vs:ident),*]) => ( + impl< $($vs),* > HeaderTryFrom<( $($vs,)* )> for OptMailboxList + where $($vs: HeaderTryInto<Mailbox>),* + { + #[allow(non_snake_case)] + fn try_from( ($($vs,)*): ($($vs,)*) ) -> Result<Self, ComponentCreationError> { + // we use the type names as variable names, + // not nice but it works + //let ($($vs),*) = src; + let mut out = Vec::new(); + $( + let $vs = $vs.try_into()?; + out.push($vs); + )* + Ok( OptMailboxList( out ) ) + } + } + ); + ([]) => (); + ([$first_vs:ident $(, $vs:ident)*]) => ( + impl_header_try_from_tuple!{ _MBoxList [$first_vs $(, $vs)* ] } + impl_header_try_from_tuple!{ _OptMBoxList [$first_vs $(, $vs)*] } + impl_header_try_from_tuple!{ [$($vs),*] } + ); +} + +impl_header_try_from_tuple! { + [ + A0, A1, A2, A3, A4, A5, A6, A7, + A8, A9, A10, A11, A12, A13, A14, A15, + A16, A17, A18, A19, A20, A21, A22, A23, + A24, A25, A26, A27, A28, A29, A30, A31 + ] +} + +impl<T> HeaderTryFrom<Vec<T>> for OptMailboxList + where T: HeaderTryInto<Mailbox> +{ + fn try_from(vec: Vec<T>) -> Result<Self, ComponentCreationError> { + let mut out = Vec::new(); + for ele in vec.into_iter() { + out.push( ele.try_into()? ); + } + Ok( OptMailboxList( out ) ) + } +} + +impl EncodableInHeader for MailboxList { + + fn encode(&self, handle: &mut EncodingWriter) -> Result<(), EncodingError> { + encode_list( self.0.iter(), handle ) + } + + fn boxed_clone(&self) -> Box<EncodableInHeader> { + Box::new(self.clone()) + } +} + +fn encode_list<'a, I>(list_iter: I, handle: &mut EncodingWriter) -> Result<(), EncodingError> + where I: Iterator<Item=&'a Mailbox> +{ + sep_for!{ mailbox in list_iter; + sep { + handle.write_char( SoftAsciiChar::from_unchecked(',') )?; + handle.write_fws(); + }; + mailbox.encode( handle )?; + } + Ok( () ) +} + +deref0!{ +mut OptMailboxList => Vec<Mailbox> } +deref0!{ +mut MailboxList => Vec1<Mailbox> } + +#[cfg(test)] +mod test { + use ::header_components::{ Mailbox, Email, Phrase }; + use super::*; + + + ec_test! { empty_list, { + OptMailboxList( Vec::new() ) + } => ascii => [ + + ]} + + ec_test! { single, { + MailboxList( vec1![ + Mailbox { + display_name: Some( Phrase::try_from( "hy ho" )? ), + email: Email::try_from( "ran@dom" )? + }, + ] ) + } => ascii => [ + Text "hy", + MarkFWS, + Text " ho", + MarkFWS, + Text " <", + MarkFWS, + Text "ran", + MarkFWS, + Text "@", + MarkFWS, + Text "dom", + MarkFWS, + Text ">" + ]} + + ec_test! { multiple, { + MailboxList( vec1![ + Mailbox { + display_name: Some( Phrase::try_from( "hy ho" )? ), + email: Email::try_from( "nar@mod" )? + }, + Mailbox { + display_name: None, + email: Email::try_from( "ran@dom" )? + } + ] ) + } => ascii => [ + Text "hy", + MarkFWS, + Text " ho", + MarkFWS, + Text " <", + MarkFWS, + Text "nar", + MarkFWS, + Text "@", + MarkFWS, + Text "mod", + MarkFWS, + Text ">,", + MarkFWS, + Text " <", + MarkFWS, + Text "ran", + MarkFWS, + Text "@", + MarkFWS, + Text "dom", + MarkFWS, + Text ">" + ]} +}
\ No newline at end of file |