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 ); #[derive(Debug, Hash, Eq, PartialEq, Clone)] pub struct MailboxList( pub Vec1 ); impl MailboxList { pub fn from_single( m: Mailbox ) -> Self { MailboxList( Vec1::new( m ) ) } } impl IntoIterator for MailboxList { type Item = as IntoIterator>::Item; type IntoIter = 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 { Box::new(self.clone()) } } //impl HeaderTryFrom for OptMailboxList { // fn try_from( mbox: Mailbox ) -> Result { // Ok( OptMailboxList( vec![ mbox ] ) ) // } //} //impl HeaderTryFrom for MailboxList // where T: HeaderTryInto //{ // fn try_from( mbox: T ) -> Result { // 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 HeaderTryFrom> for MailboxList where T: HeaderTryInto { fn try_from(vec: Vec) -> Result { try_from_into_iter( vec ) } } fn try_from_into_iter( mboxes: IT ) -> Result where IT: IntoIterator, IT::Item: HeaderTryInto { 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 HeaderTryFrom<[T; $len]> for MailboxList where T: HeaderTryInto { fn try_from( vec: [T; $len] ) -> Result { //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 HeaderTryFrom<[T; $len]> for OptMailboxList where T: HeaderTryInto { fn try_from( vec: [T; $len] ) -> Result { 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),* { #[allow(non_snake_case)] fn try_from( ($($vs,)*): ($($vs,)*) ) -> Result { // 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::try_from_vec(out).unwrap() ) ) } } ); (_OptMBoxList [$($vs:ident),*]) => ( impl< $($vs),* > HeaderTryFrom<( $($vs,)* )> for OptMailboxList where $($vs: HeaderTryInto),* { #[allow(non_snake_case)] fn try_from( ($($vs,)*): ($($vs,)*) ) -> Result { // 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 HeaderTryFrom> for OptMailboxList where T: HeaderTryInto { fn try_from(vec: Vec) -> Result { 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 { Box::new(self.clone()) } } fn encode_list<'a, I>(list_iter: I, handle: &mut EncodingWriter) -> Result<(), EncodingError> where I: Iterator { sep_for!{ mailbox in list_iter; sep { handle.write_char( SoftAsciiChar::from_unchecked(',') )?; handle.write_fws(); }; mailbox.encode( handle )?; } Ok( () ) } deref0!{ +mut OptMailboxList => Vec } deref0!{ +mut MailboxList => Vec1 } #[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 ">" ]} }