use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::ops::Deref;
use std::slice::Iter as SliceIter;
use std::sync::Arc;
use chrono::{DateTime, FixedOffset, Utc};
use super::results::{ParsingError, ParsingResult};
use super::rfc2047::decode_rfc2047;
use super::rfc822::Rfc822DateParser;
pub trait FromHeader: Sized {
fn from_header(value: String) -> ParsingResult<Self>;
}
pub trait ToHeader {
fn to_header(value: Self) -> ParsingResult<String>;
}
pub trait ToFoldedHeader {
fn to_folded_header(start_pos: usize, value: Self) -> ParsingResult<String>;
}
impl<T: ToHeader> ToFoldedHeader for T {
fn to_folded_header(_: usize, value: T) -> ParsingResult<String> {
ToHeader::to_header(value)
}
}
impl FromHeader for String {
fn from_header(value: String) -> ParsingResult<String> {
#[derive(Debug, Clone, Copy)]
enum ParseState {
Normal(usize),
SeenEquals(usize),
SeenQuestion(usize, usize),
}
let mut state = ParseState::Normal(0);
let mut decoded = String::new();
let value_slice = &value[..];
for (pos, c) in value.char_indices() {
state = match (state, c) {
(ParseState::SeenQuestion(start_pos, 4), '=') => {
let next_pos = pos + c.len_utf8();
let part_decoded = decode_rfc2047(&value_slice[start_pos..next_pos]);
let to_push = match part_decoded {
Some(ref s) => &s[..],
None => &value_slice[start_pos..pos],
};
decoded.push_str(to_push);
ParseState::Normal(next_pos)
}
(ParseState::SeenQuestion(start_pos, count), '?') => {
ParseState::SeenQuestion(start_pos, count + 1)
}
(ParseState::SeenQuestion(start_pos, count), _) => {
if count > 4 {
ParseState::Normal(start_pos)
} else {
state
}
}
(ParseState::SeenEquals(start_pos), '?') => ParseState::SeenQuestion(start_pos, 1),
(ParseState::SeenEquals(start_pos), _) => {
ParseState::Normal(start_pos)
}
(ParseState::Normal(start_pos), '=') => {
if start_pos != pos {
decoded.push_str(&value_slice[start_pos..pos]);
}
ParseState::SeenEquals(pos)
}
(ParseState::Normal(_), _) => state,
};
}
let last_start = match state {
ParseState::Normal(start_pos) => start_pos,
ParseState::SeenEquals(start_pos) => start_pos,
ParseState::SeenQuestion(start_pos, _) => start_pos,
};
decoded.push_str(&value_slice[last_start..]);
Ok(decoded)
}
}
impl FromHeader for DateTime<FixedOffset> {
fn from_header(value: String) -> ParsingResult<DateTime<FixedOffset>> {
let mut parser = Rfc822DateParser::new(&value[..]);
parser.consume_datetime()
}
}
impl FromHeader for DateTime<Utc> {
fn from_header(value: String) -> ParsingResult<DateTime<Utc>> {
let dt: ParsingResult<DateTime<FixedOffset>> = FromHeader::from_header(value);
dt.map(|i| i.with_timezone(&Utc))
}
}
impl ToHeader for String {
fn to_header(value: String) -> ParsingResult<String> {
Ok(value)
}
}
impl<'a> ToHeader for &'a str {
fn to_header(value: &'a str) -> ParsingResult<String> {
Ok(value.to_string())
}
}
#[derive(PartialEq, Eq, Clone, Debug, Hash)]
pub struct Header {
pub name: String,
value: String,
}
impl<S: Into<String>, T: Into<String>> From<(S, T)> for Header {
fn from(header: (S, T)) -> Self {
let (name, value) = header;
Header::new(name.into(), value.into())
}
}
impl Header {
pub fn new(name: String, value: String) -> Header {
Header { name, value }
}
pub fn new_with_value<T: ToFoldedHeader>(name: String, value: T) -> ParsingResult<Header> {
let header_len = name.len() + 2;
ToFoldedHeader::to_folded_header(header_len, value)
.map(|val| Header::new(name.clone(), val))
}
pub fn get_value<T: FromHeader>(&self) -> ParsingResult<T> {
FromHeader::from_header(self.value.clone())
}
}
impl fmt::Display for Header {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}: {}", self.name, self.value)
}
}
pub struct HeaderIter<'s> {
iter: SliceIter<'s, Arc<Header>>,
}
impl<'s> HeaderIter<'s> {
fn new(iter: SliceIter<'s, Arc<Header>>) -> HeaderIter<'s> {
HeaderIter { iter }
}
}
impl<'s> Iterator for HeaderIter<'s> {
type Item = &'s Header;
fn next(&mut self) -> Option<&'s Header> {
match self.iter.next() {
Some(s) => Some(s.deref()),
None => None,
}
}
}
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct HeaderMap {
ordered_headers: Vec<Arc<Header>>,
headers: HashMap<String, Vec<Arc<Header>>>,
}
impl HeaderMap {
pub fn new() -> HeaderMap {
HeaderMap {
ordered_headers: Vec::new(),
headers: HashMap::new(),
}
}
pub fn insert(&mut self, header: Header) {
let header_name = header.name.clone();
let rc = Arc::new(header);
self.ordered_headers.push(rc.clone());
match self.headers.entry(header_name) {
Entry::Occupied(mut entry) => {
entry.get_mut().push(rc);
}
Entry::Vacant(entry) => {
let mut header_list = Vec::new();
header_list.push(rc);
entry.insert(header_list);
}
};
}
pub fn replace(&mut self, header: Header) {
let header_name = header.name.clone();
let rc = Arc::new(header);
let mut i = 0;
let mut have_inserted = false;
while i < self.ordered_headers.len() {
if self.ordered_headers[i].name == header_name {
if have_inserted {
self.ordered_headers.remove(i);
} else {
self.ordered_headers[i] = rc.clone();
have_inserted = true;
}
} else {
i += 1;
}
}
let mut header_list = Vec::new();
header_list.push(rc.clone());
self.headers.insert(header_name, header_list);
}
pub fn iter(&self) -> HeaderIter {
HeaderIter::new(self.ordered_headers.iter())
}
pub fn get(&self, name: String) -> Option<&Header> {
self.headers
.get(&name)
.map(|headers| headers.last().unwrap())
.map(|rc| rc.deref())
}
pub fn get_value<T: FromHeader>(&self, name: String) -> ParsingResult<T> {
match self.get(name) {
Some(ref header) => header.get_value(),
None => Err(ParsingError::new("Couldn't find header value.".to_string())),
}
}
pub fn len(&self) -> usize {
self.ordered_headers.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn find(&self, name: &str) -> Option<Vec<&Header>> {
self.headers
.get(name)
.map(|rcs| rcs.iter().map(|rc| rc.deref()).collect())
}
}
impl Default for HeaderMap {
fn default() -> Self {
HeaderMap::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use chrono::offset::TimeZone;
use chrono::{DateTime, FixedOffset, Utc};
static SAMPLE_HEADERS: [(&'static str, &'static str); 4] = [
("Test", "Value"),
("Test", "Value 2"),
("Test-2", "Value 3"),
("Test-Multiline", "Foo\nBar"),
];
fn make_sample_headers() -> Vec<Header> {
SAMPLE_HEADERS
.iter()
.map(|&(name, value)| Header::new(name.to_string(), value.to_string()))
.collect()
}
#[test]
fn test_header_to_string() {
let header = Header::new("Test".to_string(), "Value".to_string());
assert_eq!(header.to_string(), "Test: Value".to_string());
}
#[test]
fn test_string_get_value() {
struct HeaderTest<'s> {
input: &'s str,
result: Option<&'s str>,
}
let tests = vec![
HeaderTest {
input: "Value",
result: Some("Value"),
},
HeaderTest {
input: "=?ISO-8859-1?Q?Test=20text?=",
result: Some("Test text"),
},
HeaderTest {
input: "=?ISO-8859-1?Q?Multiple?= =?utf-8?b?ZW5jb2Rpbmdz?=",
result: Some("Multiple encodings"),
},
HeaderTest {
input: "Some things with =?utf-8?b?ZW5jb2Rpbmdz?=, other things without.",
result: Some("Some things with encodings, other things without."),
},
HeaderTest {
input: "Encoding =?utf-8?q?fail",
result: Some("Encoding =?utf-8?q?fail"),
},
];
for test in tests.into_iter() {
let header = Header::new("Test".to_string(), test.input.to_string());
let string_value = header.get_value::<String>().ok();
assert_eq!(string_value, test.result.map(|s| { s.to_string() }));
}
}
#[test]
fn test_datetime_get_value() {
let header = Header::new(
"Date".to_string(),
"Wed, 17 Dec 2014 09:35:07 +0100".to_string(),
);
let dt_value = header.get_value::<DateTime<FixedOffset>>().unwrap();
assert_eq!(
dt_value,
FixedOffset::east(3600).ymd(2014, 12, 17).and_hms(9, 35, 7)
);
}
#[test]
fn test_datetime_utc_get_value() {
let header = Header::new(
"Date".to_string(),
"Wed, 17 Dec 2014 09:35:07 +0100".to_string(),
);
let dt_value = header.get_value::<DateTime<Utc>>().unwrap();
assert_eq!(dt_value, Utc.ymd(2014, 12, 17).and_hms(8, 35, 7));
}
#[test]
fn test_to_header_string() {
let header = Header::new_with_value("Test".to_string(), "Value".to_string()).unwrap();
let header_value = header.get_value::<String>().unwrap();
assert_eq!(header_value, "Value".to_string());
}
#[test]
fn test_to_header_str() {
let header = Header::new_with_value("Test".to_string(), "Value").unwrap();
let header_value = header.get_value::<String>().unwrap();
assert_eq!(header_value, "Value".to_string());
}
#[test]
fn test_header_map_len() {
let mut headers = HeaderMap::new();
for (i, header) in make_sample_headers().into_iter().enumerate() {
headers.insert(header);
assert_eq!(headers.len(), i + 1);
}
}
#[test]
fn test_header_map_iter() {
let mut headers = HeaderMap::new();
let mut expected_headers = HashSet::new();
for header in make_sample_headers().into_iter() {
headers.insert(header.clone());
expected_headers.insert(header);
}
let mut count = 0;
for header in headers.iter() {
assert!(expected_headers.contains(header));
count += 1;
}
assert_eq!(count, expected_headers.len());
}
}