mail_headers/map/
mod.rs

1//! Module containing the `HeaderMap`.
2//!
3//! It also contains some helper types like iterator types
4//! for the HeaderMap etc.
5use std::marker::PhantomData;
6use std::iter::ExactSizeIterator;
7use std::fmt::{self, Debug};
8use std::collections::HashSet;
9use std::cmp::PartialEq;
10use std::hash::{Hash, Hasher};
11
12use total_order_multi_map::{
13    self,
14    TotalOrderMultiMap,
15    EntryValues,
16    EntryValuesMut
17};
18
19use ::error::{
20    HeaderTypeError,
21    HeaderValidationError,
22    BuildInValidationError
23};
24
25use ::name::{
26    HeaderName, HasHeaderName
27};
28
29use ::header::{
30    Header, HeaderKind,
31    HeaderObj, HeaderObjTrait,
32    MaxOneMarker
33};
34
35mod into_iter;
36pub use self::into_iter::*;
37
38/// The type of an validator used to check more complex header contraints.
39///
40/// An example constraint would be if a `From` header field contains more than
41/// one mailbox a `Sender` header field is required to be present.
42pub type HeaderMapValidator = fn(&HeaderMap) -> Result<(), ::error::HeaderValidationError>;
43
44//TODO extend example to use get,get_mut etc.
45/// A header map is a collection representing a number
46/// of mail headers in an specific order.
47///
48///
49/// # Example
50///
51/// ```
52/// # #[macro_use]
53/// # extern crate mail_headers;
54///
55/// // just import all headers
56/// use mail_headers::HeaderMap;
57/// use mail_headers::headers::*;
58/// use mail_headers::error::ComponentCreationError;
59///
60/// fn create_headers() -> Result<HeaderMap, ComponentCreationError> {
61///     headers!{
62///         // from and to can have multiple values
63///         // until specialization is stable is array
64///         // is necessary
65///         _From: [("My Fancy Display Name", "theduck@example.com")],
66///         _To: [ "unknown@example.com", ],
67///         Subject: "Who are you?"
68///     }
69/// }
70///
71/// fn main() {
72///     let headers = create_headers().unwrap();
73///     assert_eq!(headers.len(), 3);
74/// }
75/// ```
76///
77/// # Note
78///
79/// A number of methods implemented on HeaderMap appear in two variations,
80/// one which accepts a type hint (a normally zero sized struct implementing
81/// HeaderKind) and on which just accepts the type and needs to be called with
82/// the turbofish operator. The later one is prefixed by a `_` as the former
83/// one is more nice to use, but in some situations, e.g. when wrapping
84/// `HeaderMap` in custom code the only type accepting variations are more
85/// useful.
86///
87/// ```rust,ignore
88/// let _ = map.get(Subject);
89/// //is equivalent to
90/// let _ = map._get::<Subject>();
91/// ```
92///
93/// # MaxOne (In-)Consistency
94///
95/// Most headers can only appear up to one time in a header section.
96/// They are marked with `H::MAX_ONE == true` and implement `MaxOneMarker`,
97/// also as object you can use `is_max_one` to check it.
98///
99/// Not only can they only appear max one time, it is normal for a user
100/// who is not aware about the other headers to expect that when you insert
101/// them into a header map which already contains them that they replace
102/// the existing header. Even more so most headers which can appear more
103/// then one time are unlikely to appear in a application of this library
104/// like e.g. all `Resent-*` header which normally get just prepended
105/// to existing mail in text format or the `Comment` header which isn't
106/// used that much.
107///
108/// Because of this it was decided that when inserting a `"max one"` header
109/// it will act as expected an replace other headers with the same name and
110/// only if a `"multi"` header is inserted it is added to all headers associated
111/// with the same name.
112///
113/// But there is a single problem. If there are multiple implementations implementations
114/// for the same header which disagree in wether or not the header is `"max one"` (which
115/// would be a bug anyway!) then this can lead to a tricky situration when you first
116/// insert the version which is `"max one"` and then the one which is `"multi"`.
117/// There had been two ways to deal with this:
118///
119/// 1. return a error when inserting in such a situation
120/// 2. simple allow it and check it when running the other
121///    validators
122///
123/// Given that a header map contains additionally validators which needs
124/// to be run explicitly to make sure that a map is valid before using it
125/// as a header section in a mail it was decided to go with the later approach.
126/// Originally the first approach was implemented but turned out to be not
127/// very ergonomic, and the second approach has little disadvantages as:
128///
129/// - it's already unlikely to run into the situation
130/// - you have to run validators anyway before using the
131///   header map
132///
133///
134/// **So yes, you can not relay on the "max one" constraints
135///   to be uphold without running the validators**
136///
137///
138#[derive(Clone)]
139pub struct HeaderMap {
140    inner_map: TotalOrderMultiMap<HeaderName, Box<HeaderObj>>,
141}
142
143pub type Iter<'a> = total_order_multi_map::Iter<'a, HeaderName, Box<HeaderObj>>;
144pub type IterMut<'a> = total_order_multi_map::IterMut<'a, HeaderName, Box<HeaderObj>>;
145pub type Values<'a> = total_order_multi_map::Values<'a, HeaderName, Box<HeaderObj>>;
146pub type ValuesMut<'a> = total_order_multi_map::ValuesMut<'a, HeaderName, Box<HeaderObj>>;
147
148impl Debug for HeaderMap {
149    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
150        write!(fter, "HeaderMap {{ ")?;
151        for (key, val_cont) in self.iter() {
152            write!(fter, "{}: {:?},", key.as_str(), val_cont)?;
153        }
154        write!(fter, " }}")
155    }
156}
157
158impl Default for HeaderMap {
159    fn default() -> Self {
160        HeaderMap {
161            inner_map: Default::default()
162        }
163    }
164}
165
166impl HeaderMap {
167
168    /// create a new empty header map
169    pub fn new() -> Self {
170        Default::default()
171    }
172
173    /// returns the number of headers in this map
174    pub fn len(&self) -> usize {
175        self.inner_map.len()
176    }
177
178    /// clears the header map
179    ///
180    /// This removes all headers _and_ all validators
181    pub fn clear(&mut self) {
182        self.inner_map.clear();
183    }
184
185    /// Iterate over all `HeaderObj` added to the map.
186    pub fn values(&self) -> Values {
187        self.inner_map.values()
188    }
189
190    /// Iterate with mut refs over all `HeaderObj` added to the map.
191    pub fn values_mut(&mut self) -> ValuesMut {
192        self.inner_map.values_mut()
193    }
194
195    /// call each unique contextual validator exactly once with this map as parameter
196    ///
197    /// If multiple Headers provide the same contextual validator (e.g. the resent headers)
198    /// it's still only called once.
199    ///
200    /// # Max One
201    ///
202    /// This will also validate that for any header name for which a header
203    /// was added with `MAX_ONE == true` it will be validated that it is the
204    /// only header for that header name.
205    pub fn use_contextual_validators(&self) -> Result<(), HeaderValidationError> {
206        let mut seen_validators = HashSet::new();
207
208        let mut validate = |validator| -> Result<(), HeaderValidationError> {
209            if let Some(validator) = validator {
210                if seen_validators.insert(ValidatorHashWrapper(validator)) {
211                    (validator)(self)?;
212                }
213            }
214            Ok(())
215        };
216
217        for mut group in self.inner_map.group_iter() {
218            let first = group.next().expect("[BUG] returned header without any headers inserted for it");
219            let max_one = first.is_max_one();
220            validate(first.validator())?;
221            let header_name = group.key().as_str();
222            for other in group {
223                if max_one != other.is_max_one() {
224                     return Err(BuildInValidationError::MaxOneInconsistency { header_name }.into());
225                }
226                validate(other.validator())?;
227            }
228        }
229        Ok(())
230    }
231
232    /// Returns true if this map contains a header with the given name.
233    pub fn contains<H: HasHeaderName>(&self, name: H) -> bool {
234        self.inner_map.contains_key(name.get_name())
235    }
236
237    /// Returns the single header associated with the given header kind.
238    ///
239    /// As this uses the `MaxOneMarker` trait which _should_ only be implemented
240    /// for `HeaderKind` impl with `MAX_ONE == true` this function can only
241    /// be used when it's fine to ignore the possible case of more than
242    /// one header of the given kind being in the same map.
243    ///
244    /// # Type Hint
245    ///
246    /// The type hint passed in is for ergonomics, e.g. so
247    /// that it's possible to write code like `map.get_single(Subject)`
248    /// if this gets in the way `_get_single` can be used which would
249    /// lead to code like `map._get_single::<Subject>()`.
250    ///
251    /// # Error
252    ///
253    /// - If there are multiple implementations for the same header and
254    ///   the inserted headers is based on a different type some `HeaderTypeError`
255    ///   is returned
256    ///
257    /// - If there are multiple implementations for the same header which
258    ///   disagree on the value of `H::MAX_ONE` (which is a bug) this can
259    ///   in some rare situations lead to be there more then one header for
260    ///   a "max one" header in the map, in which case a `HeaderTypeError`
261    ///   is returned.
262    #[inline]
263    pub fn get_single<'a, H>(&'a self, _type_hint: H)
264        -> Option<Result<&'a Header<H>, HeaderTypeError>>
265        where H: MaxOneMarker
266    {
267        self._get_single::<H>()
268    }
269
270    /// A variation of `get_single` which doesn't require passing in a type hint.
271    ///
272    /// Normally using `get_single` is more ergonomic, except if you write a function
273    /// which abstracts over it in which case using `_get_single` can be better.
274    pub fn _get_single<'a, H>(&'a self)
275        -> Option<Result<&'a Header<H>, HeaderTypeError>>
276        where H: MaxOneMarker
277    {
278        let mut bodies = self.get_untyped(H::name());
279        if bodies.len() > 1 {
280            return Some(Err(HeaderTypeError::new(H::name())))
281        }
282
283        bodies.next().map(|untyped| {
284            untyped.downcast_ref::<H>()
285                .ok_or_else(|| HeaderTypeError::new(H::name()))
286        })
287    }
288
289    /// Returns a a mutable reference to the header associated with the given header kind.__internals
290    ///
291    /// See `HeaderMap::get_single` for more details.
292    #[inline]
293    pub fn get_single_mut<H>(&mut self, _type_hint: H)
294        -> Option<Result<&mut Header<H>, HeaderTypeError>>
295        where H: MaxOneMarker
296    {
297        self._get_single_mut::<H>()
298    }
299
300    /// Returns a a mutable reference to the header associated with the given header kind.__internals
301    ///
302    /// See `HeaderMap::_get_single` for more details.
303    pub fn _get_single_mut<H>(&mut self)
304        -> Option<Result<&mut Header<H>, HeaderTypeError>>
305        where H: MaxOneMarker
306    {
307        let mut bodies = self.get_untyped_mut(H::name());
308        if bodies.len() > 1 {
309            return Some(Err(HeaderTypeError::new(H::name())))
310        }
311
312        bodies.next().map(|untyped| {
313            untyped.downcast_mut::<H>()
314                .ok_or_else(|| HeaderTypeError::new(H::name()))
315        })
316    }
317
318    /// Returns all header bodies for a given header name, without trying to cast them to a concrete type
319    ///
320    /// Accepts both `HeaderName` or a type implementing `HeaderKind`.
321    ///
322    #[inline]
323    pub fn get_untyped<H: HasHeaderName>(&self, name: H) -> UntypedBodies {
324        self.inner_map.get(name.get_name())
325    }
326
327    /// Returns all header bodies for a given header name, without trying to cast them to a concrete type
328    ///
329    /// Accepts both `HeaderName` or a type implementing `HeaderKind`.
330    ///
331    #[inline]
332    pub fn get_untyped_mut<H: HasHeaderName>(&mut self, name: H) -> UntypedBodiesMut {
333        self.inner_map.get_mut(name.get_name())
334    }
335
336    /// Returns all header bodies for a given header
337    #[inline(always)]
338    pub fn get<H>(&self, _type_hint: H) -> TypedBodies<H>
339        where H: HeaderKind
340    {
341        self._get::<H>()
342    }
343
344    /// Returns all header bodies for a given header
345    pub fn _get<H>(&self) -> TypedBodies<H>
346        where H: HeaderKind
347    {
348        self.get_untyped(H::name()).into()
349    }
350
351    /// Returns all header bodies for a given header
352    #[inline(always)]
353    pub fn get_mut<H>(&mut self, _type_hint: H) -> TypedBodiesMut<H>
354        where H: HeaderKind
355    {
356        self._get_mut::<H>()
357    }
358
359    /// Returns all header bodies for a given header
360    pub fn _get_mut<H>(&mut self) -> TypedBodiesMut<H>
361        where H: HeaderKind
362    {
363        self.get_untyped_mut(H::name()).into()
364    }
365
366    /// Inserts the given header into the map either replacing or adding to existing headers.
367    ///
368    /// - If `H::MAX_ONE` is `true` then it will use "replacing insert" which means
369    ///   all headers previously associated with the given  header (name) are removed when
370    ///   adding the new header.
371    ///
372    ///   This behavior is analog to how a normal map works and
373    ///   is what a user which isn't aware that there are some headers which can appear multiple
374    ///   times would expect. Most common headers (`Subject`, `From`, `To`, `Sender`, etc.) fall
375    ///   into this category.
376    ///
377    /// - If `H::MAX_ONE` is `false` then it will use "adding insert" which means
378    ///   that it will add the header to all headers previously associated with the given
379    ///   header name.
380    ///
381    pub fn insert<H>(&mut self, header: Header<H>)
382        where H: HeaderKind
383    {
384        let name = header.name();
385        let obj: Box<HeaderObj> = Box::new(header);
386        self._insert(name, H::MAX_ONE, obj)
387    }
388
389    /// Insert a HeaderObj into the header map.
390    #[doc(hidden)]
391    pub fn insert_untyped(&mut self, obj: Box<HeaderObj>) {
392        self._insert(obj.name(), obj.is_max_one(), obj)
393    }
394
395    #[inline(always)]
396    fn _insert(&mut self, name: HeaderName, max_one: bool, obj: Box<HeaderObj>) {
397        if max_one {
398            self.inner_map.set(name, obj);
399        } else {
400            self.inner_map.add(name, obj);
401        }
402    }
403
404    /// Insert all given headers in order into this header map.
405    ///
406    /// The insertion order of the given headers into this map
407    /// is the same as the order in which they had been inserted
408    /// into the header map through which they had been given to
409    /// this method.
410    ///
411    /// As this uses insertion it also means that headers with
412    /// `MAX_ONE == true` in the headers to insert can replace
413    /// existing headers associated with the same header name.
414    ///
415    /// # Example
416    ///
417    /// ```
418    /// # #[macro_use]
419    /// # extern crate mail_headers;
420    /// # fn main() {
421    /// use mail_headers::headers::*;
422    ///
423    /// let mut map = headers!{
424    ///     _From: [("Not Met", "it.s.me@example.com")],
425    ///     Subject: "..."
426    /// }.unwrap();
427    ///
428    /// map.insert_all(headers! {
429    ///     _To: [("You", "someone@example.com")],
430    ///     Subject: "expected subject"
431    /// }.unwrap());
432    ///
433    /// assert_eq!(map.len(), 3);
434    /// let subject = map.get_single(Subject)
435    ///     .expect("Subject to be in map (Some)")
436    ///     .expect("The type to be correct (Ok)");
437    ///
438    /// assert_eq!(subject.as_str(), "expected subject");
439    /// assert!(map.contains(_From));
440    /// assert!(map.contains(_To));
441    /// # }
442    /// ```
443    ///
444    pub fn insert_all(&mut self, other: HeaderMap) {
445        for (_name, header) in other.into_iter() {
446            self.insert_untyped(header);
447        }
448    }
449
450    /// Remove all headers with the given header name.
451    ///
452    /// Returns true, if at last one header was removed.
453    pub fn remove<H: HasHeaderName>(&mut self, name: H) -> bool {
454        self.inner_map.remove_all(name.get_name())
455    }
456
457    /// iterate over all (header name, boxed body) pairs in this map
458    pub fn iter(&self) -> Iter {
459        self.inner_map.iter()
460    }
461
462}
463
464/// Iterator over all boxed bodies for a given header name
465pub type UntypedBodies<'a> = EntryValues<'a, HeaderObj>;
466pub type UntypedBodiesMut<'a> = EntryValuesMut<'a, HeaderObj>;
467
468
469/// Iterator over all boxed bodies for a given header name with knows which type they should have
470///
471/// This iterator will automatically try to cast each header body of this
472/// header to `H::Component`, i.e. the type this body _should_ have.
473pub struct TypedBodies<'a, H>
474    where H: HeaderKind
475{
476    inner: UntypedBodies<'a>,
477    _marker: PhantomData<H>
478}
479
480impl<'a, H> From<UntypedBodies<'a>> for TypedBodies<'a, H>
481    where H: HeaderKind
482{
483    fn from(untyped: UntypedBodies<'a>) -> Self {
484        Self::new(untyped)
485    }
486}
487
488impl<'a, H> TypedBodies<'a, H>
489    where H: HeaderKind,
490{
491    fn new(inner: UntypedBodies<'a>) -> Self {
492        TypedBodies {
493            inner,
494            _marker: PhantomData
495        }
496    }
497}
498
499impl<'a, H> Iterator for TypedBodies<'a, H>
500    where H: HeaderKind
501{
502    type Item = Result<&'a Header<H>, HeaderTypeError>;
503
504    fn next(&mut self) -> Option<Self::Item> {
505        self.inner.next()
506            .map( |tobj| {
507                tobj.downcast_ref::<H>()
508                    .ok_or_else(|| HeaderTypeError::new(H::name()))
509            } )
510    }
511
512    fn size_hint(&self) -> (usize, Option<usize>) {
513        self.inner.size_hint()
514    }
515}
516
517impl<'a, H> ExactSizeIterator for TypedBodies<'a, H>
518    where H: HeaderKind
519{
520    fn len(&self) -> usize {
521        self.inner.len()
522    }
523}
524
525impl<'a, H> Clone for TypedBodies<'a, H>
526    where H: HeaderKind
527{
528    fn clone(&self) -> Self {
529        TypedBodies::new(self.inner.clone())
530    }
531}
532
533impl<'a, H> Debug for TypedBodies<'a, H>
534    where H: HeaderKind
535{
536    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
537        fter.debug_struct("TypedBodies")
538            .field("inner", &self.inner)
539            .finish()
540    }
541}
542
543/// Iterator over all boxed bodies for a given header name with knows which type they should have
544///
545/// This iterator will automatically try to cast each header body of this
546/// header to `H::Component`, i.e. the type this body _should_ have.
547pub struct TypedBodiesMut<'a, H>
548    where H: HeaderKind
549{
550    inner: UntypedBodiesMut<'a>,
551    _marker: PhantomData<H>
552}
553
554impl<'a, H> From<UntypedBodiesMut<'a>> for TypedBodiesMut<'a, H>
555    where H: HeaderKind
556{
557    fn from(untyped: UntypedBodiesMut<'a>) -> Self {
558        Self::new(untyped)
559    }
560}
561
562impl<'a, H> TypedBodiesMut<'a, H>
563    where H: HeaderKind
564{
565    fn new(inner: UntypedBodiesMut<'a>) -> Self {
566        TypedBodiesMut {
567            inner,
568            _marker: PhantomData
569        }
570    }
571}
572
573impl<'a, H> Iterator for TypedBodiesMut<'a, H>
574    where H: HeaderKind
575{
576    type Item = Result<&'a mut Header<H>, HeaderTypeError>;
577
578    fn next(&mut self) -> Option<Self::Item> {
579        self.inner.next()
580            .map(|tobj| {
581                tobj.downcast_mut::<H>()
582                    .ok_or_else(|| HeaderTypeError::new(H::name()))
583            })
584    }
585
586    fn size_hint(&self) -> (usize, Option<usize>) {
587        self.inner.size_hint()
588    }
589}
590
591impl<'a, H> ExactSizeIterator for TypedBodiesMut<'a, H>
592    where H: HeaderKind
593{
594    fn len(&self) -> usize {
595        self.inner.len()
596    }
597}
598
599impl<'a, H> Debug for TypedBodiesMut<'a, H>
600    where H: HeaderKind
601{
602    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
603        fter.write_str("TypedBodiesMut { .. }")
604    }
605}
606
607/// Create a header map from a list of header's with ther fields
608///
609/// # Example
610///
611/// ```
612/// # #[macro_use]
613/// # extern crate mail_headers;
614/// # use mail_headers::headers::*;
615/// # use mail_headers::error::ComponentCreationError;
616/// # fn main() { (|| -> Result<(), ComponentCreationError> {
617/// let map = headers! {
618///     _From: ["bobo@nana.test"],
619///     Subject: "hy there"
620/// }?;
621/// # Ok(()) })(); }
622/// ```
623#[macro_export]
624macro_rules! headers {
625    ($($header:ty : $val:expr),*) => ({
626        //FIXME[rust/catch block] use catch block once available
627        (|| -> ::std::result::Result<$crate::HeaderMap, $crate::error::ComponentCreationError> {
628            let mut map = $crate::HeaderMap::new();
629            $(
630                map.insert(<$header as $crate::HeaderKind>::auto_body($val)?);
631            )*
632            Ok(map)
633        })()
634    });
635}
636
637/// HeaderMapValidator is just a function pointer,
638/// but it does not implement Hash so we wrap it
639/// and implement Hash on it. Note that some function
640/// pointers implement Hash/Eq and other doesn't,
641/// which is caused by some limitations with wildcard
642/// implementations
643#[derive(Copy, Clone)]
644struct ValidatorHashWrapper(HeaderMapValidator);
645
646impl ValidatorHashWrapper {
647
648    fn identity_repr(&self) -> usize {
649        self.0 as usize
650    }
651}
652
653impl PartialEq<Self> for ValidatorHashWrapper {
654    fn eq(&self, other: &Self) -> bool {
655        self.identity_repr() == other.identity_repr()
656    }
657}
658
659impl Eq for ValidatorHashWrapper {}
660
661impl Debug for ValidatorHashWrapper {
662    fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
663        write!(fter, "ValidatorHashWrapper(0x{:x})", self.identity_repr())
664    }
665}
666
667impl Hash for ValidatorHashWrapper {
668    fn hash<H: Hasher>(&self, state: &mut H) {
669        state.write_usize(self.identity_repr())
670    }
671}
672
673
674pub fn check_header_count_max_one(name: HeaderName, map: &HeaderMap)
675    -> Result<(), HeaderValidationError>
676{
677    let valid = map.get_untyped(name).len() <= 1;
678    if valid {
679        Ok(())
680    } else {
681        Err(HeaderValidationError::from(
682            BuildInValidationError::MoreThenOne {
683                header_name: name.as_str()
684            }
685        ))
686    }
687}
688
689#[cfg(test)]
690mod test {
691    use failure::Context;
692    use soft_ascii_string::SoftAsciiStr;
693
694    use internals::error::{EncodingError, EncodingErrorKind};
695    use internals::encoder::{EncodableInHeader, EncodingWriter};
696
697    use ::HeaderTryFrom;
698    use ::error::{ComponentCreationError, HeaderValidationError};
699    use ::header_components::RawUnstructured;
700
701    use super::*;
702
703    use self::good_headers::*;
704    use self::bad_headers::{
705        Subject as BadSubject,
706        Comments as BadComments
707    };
708    use self::bad_headers2::{
709        Comments2 as BadComments2
710    };
711
712    #[derive(Debug, Clone, Eq, PartialEq, Hash)]
713    pub struct OtherComponent;
714
715    impl HeaderTryFrom<()> for OtherComponent {
716        fn try_from(_: ()) -> Result<OtherComponent, ComponentCreationError> {
717            Ok(OtherComponent)
718        }
719    }
720    impl EncodableInHeader for OtherComponent {
721        fn encode(&self, _encoder:  &mut EncodingWriter) -> Result<(), EncodingError> {
722            Err(EncodingError::from(
723                    EncodingErrorKind::Other { kind: "encoding is not implemented" }))
724        }
725
726        fn boxed_clone(&self) -> Box<EncodableInHeader> {
727            Box::new(self.clone())
728        }
729    }
730
731
732    mod good_headers {
733        use ::header_components;
734        def_headers! {
735            test_name: validate_header_names,
736            scope: header_components,
737            Subject, unchecked { "Subject" }, RawUnstructured, maxOne, None,
738            Comments, unchecked { "Comments" }, RawUnstructured, multi, None
739        }
740    }
741
742    mod bad_headers {
743        def_headers! {
744            test_name: validate_header_names,
745            scope: super,
746            Subject, unchecked { "Subject" },  OtherComponent, maxOne, None,
747            Comments, unchecked { "Comments" }, OtherComponent, multi, None
748        }
749    }
750    mod bad_headers2 {
751        def_headers! {
752            test_name: validate_header_names2,
753            scope: super,
754            Comments2, unchecked { "Comments" }, OtherComponent, maxOne, None
755        }
756    }
757
758    const TEXT_1: &str = "Random stuff XD";
759    const TEXT_2: &str = "Having a log of fun, yes a log!";
760
761    test!(max_one_mixup {
762        let headers = headers! {
763            BadComments2: (),
764            BadComments: ()
765        }?;
766
767        let res = headers.use_contextual_validators();
768        if let Err(HeaderValidationError::BuildIn(berr)) = res {
769            if let BuildInValidationError::MaxOneInconsistency { ..} = berr.get_context() {
770                return Ok(());
771            }
772            panic!("unexpected error: {:?}", berr);
773        }
774        panic!("unexpected result: {:?}", res);
775    });
776
777    #[test]
778    fn headers_macro() {
779        let headers = headers! {
780            Comments: TEXT_1,
781            Subject: TEXT_2
782        }.unwrap();
783
784
785        let count = headers
786            // all headers _could_ have multiple values, through neither
787            // ContentType nor Subject do have multiple value
788            .get(Comments)
789            .map(|h: Result<&Header<Comments>, HeaderTypeError>| {
790                let v = h.expect( "the trait object to be downcastable to Header<Comments>" );
791                assert_eq!(v.as_str(), TEXT_1);
792            })
793            .count();
794        assert_eq!(1, count);
795
796        let count = headers
797            .get(Subject)
798            .map(|h: Result<&Header<Subject>, HeaderTypeError>| {
799                let val = h.expect( "the trait object to be downcastable to Header<Subject>" );
800                assert_eq!(val.as_str(), TEXT_2);
801            })
802            .count();
803        assert_eq!(1, count);
804    }
805
806    #[test]
807    fn get_single() {
808        let headers = headers! {
809            Subject: "abc"
810        }.unwrap();
811
812        assert_eq!(
813            "abc",
814            headers.get_single(Subject)
815                .unwrap()//Some
816                .unwrap()//Result
817                .as_str()
818        );
819    }
820
821    #[test]
822    fn get_single_cast_error() {
823        let headers = headers! {
824            Subject: "abc"
825        }.unwrap();
826
827        let res = headers.get_single(BadSubject);
828        assert_err!( res.expect("where did the header go?") );
829    }
830
831    #[test]
832    fn get() {
833        let headers = headers! {
834            Subject: "abc",
835            Comments: "1st",
836            BadComments: ()
837        }.unwrap();
838
839
840        let mut res = headers.get(Comments);
841
842        assert_eq!(res.size_hint(), (2, Some(2)));
843
844        assert_eq!(
845            "1st",
846            assert_ok!(res.next().unwrap()).as_str()
847        );
848
849        assert_err!(res.next().unwrap());
850
851        assert!( res.next().is_none() )
852
853    }
854
855    #[test]
856    fn get_untyped() {
857        let headers = headers! {
858            Subject: "abc",
859            Comments: "1st",
860            BadComments: ()
861        }.unwrap();
862
863
864        let res = headers.get_untyped(Subject::name())
865            .map(|entry| entry.downcast_ref::<Subject>().unwrap().as_str() )
866            .collect::<Vec<_>>();
867
868        assert_eq!(
869            res.as_slice(),
870            &[ "abc" ]
871        );
872
873        let mut res = headers.get_untyped(Comments::name());
874
875        assert_eq!((2, Some(2)), res.size_hint());
876
877        assert_eq!(
878            res.next().unwrap().downcast_ref::<Comments>().unwrap().as_str(),
879            "1st"
880        );
881
882        assert_eq!((1, Some(1)), res.size_hint());
883
884        assert_eq!(
885            res.next().unwrap().downcast_ref::<BadComments>().unwrap().body(),
886            &OtherComponent
887        );
888
889        assert!(res.next().is_none());
890    }
891
892    #[test]
893    fn fmt_debug() {
894        let headers = headers! {
895            Subject: "hy there"
896        }.unwrap();
897
898        let res = format!("{:?}", headers);
899        assert_eq!(
900            "HeaderMap { Subject: RawUnstructured { text: Input(Owned(\"hy there\")) }, }",
901            res.as_str()
902        );
903    }
904
905    test!(combine_keeps_order {
906        let mut headers = headers! {
907            XComment: "ab@c"
908        }?;
909
910        headers.insert_all(headers! {
911            Subject: "hy there",
912            Comments: "magic+spell"
913        }?);
914
915        assert_eq!(
916            &[
917                "X-Comment",
918                "Subject",
919                "Comments"
920            ],
921            headers.into_iter()
922                .map(|(name, _val)| name.as_str())
923                .collect::<Vec<_>>()
924                .as_slice()
925        );
926    });
927
928
929    test!(remove_1 {
930        let mut headers = headers!{
931            Comments: "a",
932            Subject: "b",
933            Comments: "c",
934            Comments: "d"
935        }?;
936
937        assert_eq!( false, headers.remove(XComment::name()));
938        assert_eq!( true, headers.remove(Subject::name()));
939
940        assert_eq!( 3, headers.iter().count() );
941
942        let values = headers.get(Comments)
943            .map(|comp| comp.unwrap().as_str() )
944            .collect::<Vec<_>>();
945
946        assert_eq!(
947            &[ "a", "c", "d" ],
948            values.as_slice()
949        );
950    });
951
952    test!(remove_2 {
953        let mut headers = headers!{
954            Comments: "a",
955            Subject: "b",
956            Comments: "c",
957            Comments: "d"
958        }?;
959
960        assert_eq!(true, headers.remove(Comments::name()));
961        assert_eq!(false, headers.remove(Comments::name()));
962
963        assert_eq!(1, headers.iter().count());
964
965        let values = headers.get(Subject)
966            .map(|comp| comp.unwrap().as_str())
967            .collect::<Vec<_>>();
968
969        assert_eq!(
970            &[ "b" ],
971            values.as_slice()
972        );
973    });
974
975    #[derive(Default, Copy, Clone)]
976    struct XComment;
977    impl HeaderKind for XComment {
978        type Component = RawUnstructured;
979
980        fn name() -> HeaderName {
981            HeaderName::new(SoftAsciiStr::from_unchecked("X-Comment")).unwrap()
982        }
983
984        const VALIDATOR: Option<
985            fn(&HeaderMap)-> Result<(), HeaderValidationError>
986        > = Some(__validator);
987
988        const MAX_ONE: bool = false;
989    }
990
991    //some stupid but simple validator
992    fn __validator(map: &HeaderMap) -> Result<(), HeaderValidationError> {
993        if map.get_untyped(Comments::name()).len() != 0 {
994            return Err(HeaderValidationError::Custom(
995                Context::new("can't have X-Comment and Comments in same mail")
996                .into()
997            ));
998        }
999        Ok(())
1000    }
1001
1002    test!(contains_works {
1003        let map = headers! {
1004            Subject: "soso"
1005        }?;
1006
1007        assert_eq!( true,  map.contains( Subject::name()  ));
1008        assert_eq!( true,  map.contains( Subject          ));
1009        assert_eq!( false, map.contains( Comments::name() ));
1010        assert_eq!( false, map.contains( Comments         ));
1011    });
1012
1013    test!(use_validator_ok {
1014        let map = headers! {
1015            XComment: "yay",
1016            Subject: "soso"
1017        }?;
1018
1019        assert_ok!(map.use_contextual_validators());
1020    });
1021
1022    test!(use_validator_err {
1023        let map = headers! {
1024            XComment: "yay",
1025            Comments: "oh no",
1026            Subject: "soso"
1027        }?;
1028
1029        assert_err!(map.use_contextual_validators());
1030    });
1031
1032    test!(has_len {
1033        let map = headers! {
1034            XComment: "yay",
1035            Comments: "oh no",
1036            Subject: "soso"
1037        }?;
1038
1039        assert_eq!(3, map.len());
1040    });
1041
1042    test!(does_not_conflic_with_custom_result_type {
1043        #[allow(unused)]
1044        type Result<T> = ::std::result::Result<T, ()>;
1045
1046        let map = headers! {
1047            Subject: "yay"
1048        }?;
1049
1050        assert_eq!(1, map.len());
1051    });
1052}