use std::marker::PhantomData;
use std::iter::ExactSizeIterator;
use std::fmt::{self, Debug};
use std::collections::HashSet;
use std::cmp::PartialEq;
use std::hash::{Hash, Hasher};
use total_order_multi_map::{
self,
TotalOrderMultiMap,
EntryValues,
EntryValuesMut
};
use ::error::{
HeaderTypeError,
HeaderValidationError,
BuildInValidationError
};
use ::name::{
HeaderName, HasHeaderName
};
use ::header::{
Header, HeaderKind,
HeaderObj, HeaderObjTrait,
MaxOneMarker
};
mod into_iter;
pub use self::into_iter::*;
pub type HeaderMapValidator = fn(&HeaderMap) -> Result<(), ::error::HeaderValidationError>;
#[derive(Clone)]
pub struct HeaderMap {
inner_map: TotalOrderMultiMap<HeaderName, Box<HeaderObj>>,
}
pub type Iter<'a> = total_order_multi_map::Iter<'a, HeaderName, Box<HeaderObj>>;
pub type IterMut<'a> = total_order_multi_map::IterMut<'a, HeaderName, Box<HeaderObj>>;
pub type Values<'a> = total_order_multi_map::Values<'a, HeaderName, Box<HeaderObj>>;
pub type ValuesMut<'a> = total_order_multi_map::ValuesMut<'a, HeaderName, Box<HeaderObj>>;
impl Debug for HeaderMap {
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
write!(fter, "HeaderMap {{ ")?;
for (key, val_cont) in self.iter() {
write!(fter, "{}: {:?},", key.as_str(), val_cont)?;
}
write!(fter, " }}")
}
}
impl Default for HeaderMap {
fn default() -> Self {
HeaderMap {
inner_map: Default::default()
}
}
}
impl HeaderMap {
pub fn new() -> Self {
Default::default()
}
pub fn len(&self) -> usize {
self.inner_map.len()
}
pub fn clear(&mut self) {
self.inner_map.clear();
}
pub fn values(&self) -> Values {
self.inner_map.values()
}
pub fn values_mut(&mut self) -> ValuesMut {
self.inner_map.values_mut()
}
pub fn use_contextual_validators(&self) -> Result<(), HeaderValidationError> {
let mut seen_validators = HashSet::new();
let mut validate = |validator| -> Result<(), HeaderValidationError> {
if let Some(validator) = validator {
if seen_validators.insert(ValidatorHashWrapper(validator)) {
(validator)(self)?;
}
}
Ok(())
};
for mut group in self.inner_map.group_iter() {
let first = group.next().expect("[BUG] returned header without any headers inserted for it");
let max_one = first.is_max_one();
validate(first.validator())?;
let header_name = group.key().as_str();
for other in group {
if max_one != other.is_max_one() {
return Err(BuildInValidationError::MaxOneInconsistency { header_name }.into());
}
validate(other.validator())?;
}
}
Ok(())
}
pub fn contains<H: HasHeaderName>(&self, name: H) -> bool {
self.inner_map.contains_key(name.get_name())
}
#[inline]
pub fn get_single<'a, H>(&'a self, _type_hint: H)
-> Option<Result<&'a Header<H>, HeaderTypeError>>
where H: MaxOneMarker
{
self._get_single::<H>()
}
pub fn _get_single<'a, H>(&'a self)
-> Option<Result<&'a Header<H>, HeaderTypeError>>
where H: MaxOneMarker
{
let mut bodies = self.get_untyped(H::name());
if bodies.len() > 1 {
return Some(Err(HeaderTypeError::new(H::name())))
}
bodies.next().map(|untyped| {
untyped.downcast_ref::<H>()
.ok_or_else(|| HeaderTypeError::new(H::name()))
})
}
#[inline]
pub fn get_single_mut<H>(&mut self, _type_hint: H)
-> Option<Result<&mut Header<H>, HeaderTypeError>>
where H: MaxOneMarker
{
self._get_single_mut::<H>()
}
pub fn _get_single_mut<H>(&mut self)
-> Option<Result<&mut Header<H>, HeaderTypeError>>
where H: MaxOneMarker
{
let mut bodies = self.get_untyped_mut(H::name());
if bodies.len() > 1 {
return Some(Err(HeaderTypeError::new(H::name())))
}
bodies.next().map(|untyped| {
untyped.downcast_mut::<H>()
.ok_or_else(|| HeaderTypeError::new(H::name()))
})
}
#[inline]
pub fn get_untyped<H: HasHeaderName>(&self, name: H) -> UntypedBodies {
self.inner_map.get(name.get_name())
}
#[inline]
pub fn get_untyped_mut<H: HasHeaderName>(&mut self, name: H) -> UntypedBodiesMut {
self.inner_map.get_mut(name.get_name())
}
#[inline(always)]
pub fn get<H>(&self, _type_hint: H) -> TypedBodies<H>
where H: HeaderKind
{
self._get::<H>()
}
pub fn _get<H>(&self) -> TypedBodies<H>
where H: HeaderKind
{
self.get_untyped(H::name()).into()
}
#[inline(always)]
pub fn get_mut<H>(&mut self, _type_hint: H) -> TypedBodiesMut<H>
where H: HeaderKind
{
self._get_mut::<H>()
}
pub fn _get_mut<H>(&mut self) -> TypedBodiesMut<H>
where H: HeaderKind
{
self.get_untyped_mut(H::name()).into()
}
pub fn insert<H>(&mut self, header: Header<H>)
where H: HeaderKind
{
let name = header.name();
let obj: Box<HeaderObj> = Box::new(header);
self._insert(name, H::MAX_ONE, obj)
}
#[doc(hidden)]
pub fn insert_untyped(&mut self, obj: Box<HeaderObj>) {
self._insert(obj.name(), obj.is_max_one(), obj)
}
#[inline(always)]
fn _insert(&mut self, name: HeaderName, max_one: bool, obj: Box<HeaderObj>) {
if max_one {
self.inner_map.set(name, obj);
} else {
self.inner_map.add(name, obj);
}
}
pub fn insert_all(&mut self, other: HeaderMap) {
for (_name, header) in other.into_iter() {
self.insert_untyped(header);
}
}
pub fn remove<H: HasHeaderName>(&mut self, name: H) -> bool {
self.inner_map.remove_all(name.get_name())
}
pub fn iter(&self) -> Iter {
self.inner_map.iter()
}
}
pub type UntypedBodies<'a> = EntryValues<'a, HeaderObj>;
pub type UntypedBodiesMut<'a> = EntryValuesMut<'a, HeaderObj>;
pub struct TypedBodies<'a, H>
where H: HeaderKind
{
inner: UntypedBodies<'a>,
_marker: PhantomData<H>
}
impl<'a, H> From<UntypedBodies<'a>> for TypedBodies<'a, H>
where H: HeaderKind
{
fn from(untyped: UntypedBodies<'a>) -> Self {
Self::new(untyped)
}
}
impl<'a, H> TypedBodies<'a, H>
where H: HeaderKind,
{
fn new(inner: UntypedBodies<'a>) -> Self {
TypedBodies {
inner,
_marker: PhantomData
}
}
}
impl<'a, H> Iterator for TypedBodies<'a, H>
where H: HeaderKind
{
type Item = Result<&'a Header<H>, HeaderTypeError>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
.map( |tobj| {
tobj.downcast_ref::<H>()
.ok_or_else(|| HeaderTypeError::new(H::name()))
} )
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, H> ExactSizeIterator for TypedBodies<'a, H>
where H: HeaderKind
{
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'a, H> Clone for TypedBodies<'a, H>
where H: HeaderKind
{
fn clone(&self) -> Self {
TypedBodies::new(self.inner.clone())
}
}
impl<'a, H> Debug for TypedBodies<'a, H>
where H: HeaderKind
{
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
fter.debug_struct("TypedBodies")
.field("inner", &self.inner)
.finish()
}
}
pub struct TypedBodiesMut<'a, H>
where H: HeaderKind
{
inner: UntypedBodiesMut<'a>,
_marker: PhantomData<H>
}
impl<'a, H> From<UntypedBodiesMut<'a>> for TypedBodiesMut<'a, H>
where H: HeaderKind
{
fn from(untyped: UntypedBodiesMut<'a>) -> Self {
Self::new(untyped)
}
}
impl<'a, H> TypedBodiesMut<'a, H>
where H: HeaderKind
{
fn new(inner: UntypedBodiesMut<'a>) -> Self {
TypedBodiesMut {
inner,
_marker: PhantomData
}
}
}
impl<'a, H> Iterator for TypedBodiesMut<'a, H>
where H: HeaderKind
{
type Item = Result<&'a mut Header<H>, HeaderTypeError>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
.map(|tobj| {
tobj.downcast_mut::<H>()
.ok_or_else(|| HeaderTypeError::new(H::name()))
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, H> ExactSizeIterator for TypedBodiesMut<'a, H>
where H: HeaderKind
{
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'a, H> Debug for TypedBodiesMut<'a, H>
where H: HeaderKind
{
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
fter.write_str("TypedBodiesMut { .. }")
}
}
#[macro_export]
macro_rules! headers {
($($header:ty : $val:expr),*) => ({
(|| -> ::std::result::Result<$crate::HeaderMap, $crate::error::ComponentCreationError> {
let mut map = $crate::HeaderMap::new();
$(
map.insert(<$header as $crate::HeaderKind>::auto_body($val)?);
)*
Ok(map)
})()
});
}
#[derive(Copy, Clone)]
struct ValidatorHashWrapper(HeaderMapValidator);
impl ValidatorHashWrapper {
fn identity_repr(&self) -> usize {
self.0 as usize
}
}
impl PartialEq<Self> for ValidatorHashWrapper {
fn eq(&self, other: &Self) -> bool {
self.identity_repr() == other.identity_repr()
}
}
impl Eq for ValidatorHashWrapper {}
impl Debug for ValidatorHashWrapper {
fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
write!(fter, "ValidatorHashWrapper(0x{:x})", self.identity_repr())
}
}
impl Hash for ValidatorHashWrapper {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_usize(self.identity_repr())
}
}
pub fn check_header_count_max_one(name: HeaderName, map: &HeaderMap)
-> Result<(), HeaderValidationError>
{
let valid = map.get_untyped(name).len() <= 1;
if valid {
Ok(())
} else {
Err(HeaderValidationError::from(
BuildInValidationError::MoreThenOne {
header_name: name.as_str()
}
))
}
}
#[cfg(test)]
mod test {
use failure::Context;
use soft_ascii_string::SoftAsciiStr;
use internals::error::{EncodingError, EncodingErrorKind};
use internals::encoder::{EncodableInHeader, EncodingWriter};
use ::HeaderTryFrom;
use ::error::{ComponentCreationError, HeaderValidationError};
use ::header_components::RawUnstructured;
use super::*;
use self::good_headers::*;
use self::bad_headers::{
Subject as BadSubject,
Comments as BadComments
};
use self::bad_headers2::{
Comments2 as BadComments2
};
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct OtherComponent;
impl HeaderTryFrom<()> for OtherComponent {
fn try_from(_: ()) -> Result<OtherComponent, ComponentCreationError> {
Ok(OtherComponent)
}
}
impl EncodableInHeader for OtherComponent {
fn encode(&self, _encoder: &mut EncodingWriter) -> Result<(), EncodingError> {
Err(EncodingError::from(
EncodingErrorKind::Other { kind: "encoding is not implemented" }))
}
fn boxed_clone(&self) -> Box<EncodableInHeader> {
Box::new(self.clone())
}
}
mod good_headers {
use ::header_components;
def_headers! {
test_name: validate_header_names,
scope: header_components,
Subject, unchecked { "Subject" }, RawUnstructured, maxOne, None,
Comments, unchecked { "Comments" }, RawUnstructured, multi, None
}
}
mod bad_headers {
def_headers! {
test_name: validate_header_names,
scope: super,
Subject, unchecked { "Subject" }, OtherComponent, maxOne, None,
Comments, unchecked { "Comments" }, OtherComponent, multi, None
}
}
mod bad_headers2 {
def_headers! {
test_name: validate_header_names2,
scope: super,
Comments2, unchecked { "Comments" }, OtherComponent, maxOne, None
}
}
const TEXT_1: &str = "Random stuff XD";
const TEXT_2: &str = "Having a log of fun, yes a log!";
test!(max_one_mixup {
let headers = headers! {
BadComments2: (),
BadComments: ()
}?;
let res = headers.use_contextual_validators();
if let Err(HeaderValidationError::BuildIn(berr)) = res {
if let BuildInValidationError::MaxOneInconsistency { ..} = berr.get_context() {
return Ok(());
}
panic!("unexpected error: {:?}", berr);
}
panic!("unexpected result: {:?}", res);
});
#[test]
fn headers_macro() {
let headers = headers! {
Comments: TEXT_1,
Subject: TEXT_2
}.unwrap();
let count = headers
.get(Comments)
.map(|h: Result<&Header<Comments>, HeaderTypeError>| {
let v = h.expect( "the trait object to be downcastable to Header<Comments>" );
assert_eq!(v.as_str(), TEXT_1);
})
.count();
assert_eq!(1, count);
let count = headers
.get(Subject)
.map(|h: Result<&Header<Subject>, HeaderTypeError>| {
let val = h.expect( "the trait object to be downcastable to Header<Subject>" );
assert_eq!(val.as_str(), TEXT_2);
})
.count();
assert_eq!(1, count);
}
#[test]
fn get_single() {
let headers = headers! {
Subject: "abc"
}.unwrap();
assert_eq!(
"abc",
headers.get_single(Subject)
.unwrap()
.unwrap()
.as_str()
);
}
#[test]
fn get_single_cast_error() {
let headers = headers! {
Subject: "abc"
}.unwrap();
let res = headers.get_single(BadSubject);
assert_err!( res.expect("where did the header go?") );
}
#[test]
fn get() {
let headers = headers! {
Subject: "abc",
Comments: "1st",
BadComments: ()
}.unwrap();
let mut res = headers.get(Comments);
assert_eq!(res.size_hint(), (2, Some(2)));
assert_eq!(
"1st",
assert_ok!(res.next().unwrap()).as_str()
);
assert_err!(res.next().unwrap());
assert!( res.next().is_none() )
}
#[test]
fn get_untyped() {
let headers = headers! {
Subject: "abc",
Comments: "1st",
BadComments: ()
}.unwrap();
let res = headers.get_untyped(Subject::name())
.map(|entry| entry.downcast_ref::<Subject>().unwrap().as_str() )
.collect::<Vec<_>>();
assert_eq!(
res.as_slice(),
&[ "abc" ]
);
let mut res = headers.get_untyped(Comments::name());
assert_eq!((2, Some(2)), res.size_hint());
assert_eq!(
res.next().unwrap().downcast_ref::<Comments>().unwrap().as_str(),
"1st"
);
assert_eq!((1, Some(1)), res.size_hint());
assert_eq!(
res.next().unwrap().downcast_ref::<BadComments>().unwrap().body(),
&OtherComponent
);
assert!(res.next().is_none());
}
#[test]
fn fmt_debug() {
let headers = headers! {
Subject: "hy there"
}.unwrap();
let res = format!("{:?}", headers);
assert_eq!(
"HeaderMap { Subject: RawUnstructured { text: Input(Owned(\"hy there\")) }, }",
res.as_str()
);
}
test!(combine_keeps_order {
let mut headers = headers! {
XComment: "ab@c"
}?;
headers.insert_all(headers! {
Subject: "hy there",
Comments: "magic+spell"
}?);
assert_eq!(
&[
"X-Comment",
"Subject",
"Comments"
],
headers.into_iter()
.map(|(name, _val)| name.as_str())
.collect::<Vec<_>>()
.as_slice()
);
});
test!(remove_1 {
let mut headers = headers!{
Comments: "a",
Subject: "b",
Comments: "c",
Comments: "d"
}?;
assert_eq!( false, headers.remove(XComment::name()));
assert_eq!( true, headers.remove(Subject::name()));
assert_eq!( 3, headers.iter().count() );
let values = headers.get(Comments)
.map(|comp| comp.unwrap().as_str() )
.collect::<Vec<_>>();
assert_eq!(
&[ "a", "c", "d" ],
values.as_slice()
);
});
test!(remove_2 {
let mut headers = headers!{
Comments: "a",
Subject: "b",
Comments: "c",
Comments: "d"
}?;
assert_eq!(true, headers.remove(Comments::name()));
assert_eq!(false, headers.remove(Comments::name()));
assert_eq!(1, headers.iter().count());
let values = headers.get(Subject)
.map(|comp| comp.unwrap().as_str())
.collect::<Vec<_>>();
assert_eq!(
&[ "b" ],
values.as_slice()
);
});
#[derive(Default, Copy, Clone)]
struct XComment;
impl HeaderKind for XComment {
type Component = RawUnstructured;
fn name() -> HeaderName {
HeaderName::new(SoftAsciiStr::from_unchecked("X-Comment")).unwrap()
}
const VALIDATOR: Option<
fn(&HeaderMap)-> Result<(), HeaderValidationError>
> = Some(__validator);
const MAX_ONE: bool = false;
}
fn __validator(map: &HeaderMap) -> Result<(), HeaderValidationError> {
if map.get_untyped(Comments::name()).len() != 0 {
return Err(HeaderValidationError::Custom(
Context::new("can't have X-Comment and Comments in same mail")
.into()
));
}
Ok(())
}
test!(contains_works {
let map = headers! {
Subject: "soso"
}?;
assert_eq!( true, map.contains( Subject::name() ));
assert_eq!( true, map.contains( Subject ));
assert_eq!( false, map.contains( Comments::name() ));
assert_eq!( false, map.contains( Comments ));
});
test!(use_validator_ok {
let map = headers! {
XComment: "yay",
Subject: "soso"
}?;
assert_ok!(map.use_contextual_validators());
});
test!(use_validator_err {
let map = headers! {
XComment: "yay",
Comments: "oh no",
Subject: "soso"
}?;
assert_err!(map.use_contextual_validators());
});
test!(has_len {
let map = headers! {
XComment: "yay",
Comments: "oh no",
Subject: "soso"
}?;
assert_eq!(3, map.len());
});
test!(does_not_conflic_with_custom_result_type {
#[allow(unused)]
type Result<T> = ::std::result::Result<T, ()>;
let map = headers! {
Subject: "yay"
}?;
assert_eq!(1, map.len());
});
}