diff --git a/src/lib.rs b/src/lib.rs index a10b03a..96c01ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,6 @@ use std::collections::BTreeMap; use encoding::Encoding; -#[macro_use] -mod macros; mod dateparse; pub use dateparse::dateparse; @@ -95,11 +93,9 @@ pub struct MailHeader<'a> { } fn is_boundary(line: &str, ix: Option) -> bool { - ix.map(|v| { - line.chars().nth(v).map_or(true, |c| { - c.is_whitespace() || c == '"' || c == '(' || c == ')' || c == '<' || c == '>' - }) - }).unwrap_or(true) + ix.and_then(|v| line.chars().nth(v)) + .map(|c| c.is_whitespace() || c == '"' || c == '(' || c == ')' || c == '<' || c == '>') + .unwrap_or(true) } fn find_from(line: &str, ix_start: usize, key: &str) -> Option { @@ -142,25 +138,22 @@ fn test_find_from_u8() { impl<'a> MailHeader<'a> { /// Get the name of the header. Note that header names are case-insensitive. pub fn get_key(&self) -> Result { - Ok( - try!(encoding::all::ISO_8859_1.decode( - self.key, - encoding::DecoderTrap::Strict, - )).trim() - .to_string(), - ) + encoding::all::ISO_8859_1 + .decode(self.key, encoding::DecoderTrap::Strict) + .map(|s| s.trim().to_string()) + .map_err(|e| e.into()) } fn decode_word(&self, encoded: &str) -> Option { - let ix_delim1 = try_none!(encoded.find('?')); - let ix_delim2 = try_none!(find_from(encoded, ix_delim1 + 1, "?")); + let ix_delim1 = encoded.find('?')?; + let ix_delim2 = find_from(encoded, ix_delim1 + 1, "?")?; let charset = &encoded[0..ix_delim1]; let transfer_coding = &encoded[ix_delim1 + 1..ix_delim2]; let input = &encoded[ix_delim2 + 1..]; let decoded = match transfer_coding { - "B" | "b" => try_none!(base64::decode(input.as_bytes()).ok()), + "B" | "b" => base64::decode(input.as_bytes()).ok()?, "Q" | "q" => { // The quoted_printable module does a trim_right on the input, so if // that affects the output we should save and restore the trailing @@ -174,11 +167,11 @@ impl<'a> MailHeader<'a> { to_decode[trimmed.len()..].as_bytes(), ); } - try_none!(d.ok()) + d.ok()? } _ => return None, }; - let charset_conv = try_none!(encoding::label::encoding_from_whatwg_label(charset)); + let charset_conv = encoding::label::encoding_from_whatwg_label(charset)?; charset_conv .decode(&decoded, encoding::DecoderTrap::Replace) .ok() @@ -199,10 +192,7 @@ impl<'a> MailHeader<'a> { /// ``` pub fn get_value(&self) -> Result { let mut result = String::new(); - let chars = try!(encoding::all::ISO_8859_1.decode( - self.value, - encoding::DecoderTrap::Strict, - )); + let chars = encoding::all::ISO_8859_1.decode(self.value, encoding::DecoderTrap::Strict)?; let mut lines = chars.lines(); let mut add_space = false; while let Some(line) = lines.next().map(str::trim_left) { @@ -411,7 +401,7 @@ pub trait MailHeaderMap { impl<'a> MailHeaderMap for Vec> { fn get_first_value(&self, key: &str) -> Result, MailParseError> { for x in self { - if try!(x.get_key()).eq_ignore_ascii_case(key) { + if x.get_key()?.eq_ignore_ascii_case(key) { return x.get_value().map(Some); } } @@ -421,8 +411,8 @@ impl<'a> MailHeaderMap for Vec> { fn get_all_values(&self, key: &str) -> Result, MailParseError> { let mut values: Vec = Vec::new(); for x in self { - if try!(x.get_key()).eq_ignore_ascii_case(key) { - values.push(try!(x.get_value())); + if x.get_key()?.eq_ignore_ascii_case(key) { + values.push(x.get_value()?); } } Ok(values) @@ -472,7 +462,7 @@ pub fn parse_headers(raw_data: &[u8]) -> Result<(Vec, usize), MailPa )); } } - let (header, ix_next) = try!(parse_header(&raw_data[ix..])); + let (header, ix_next) = parse_header(&raw_data[ix..])?; headers.push(header); ix += ix_next; } @@ -667,11 +657,8 @@ impl<'a> ParsedMail<'a> { let decoded = self.get_body_raw()?; let charset_conv = encoding::label::encoding_from_whatwg_label(&self.ctype.charset) .unwrap_or(encoding::all::ASCII); - let str_body = try!(charset_conv.decode( - &decoded, - encoding::DecoderTrap::Replace, - )); - Ok(str_body) + charset_conv.decode(&decoded, encoding::DecoderTrap::Replace) + .map_err(|e| e.into()) } /// Get the body of the message as a Rust Vec. This function tries to @@ -689,24 +676,23 @@ impl<'a> ParsedMail<'a> { /// assert_eq!(p.get_body_raw().unwrap(), b"This is the body"); /// ``` pub fn get_body_raw(&self) -> Result, MailParseError> { - let transfer_coding = try!(self.headers.get_first_value("Content-Transfer-Encoding")) + let transfer_coding = self + .headers + .get_first_value("Content-Transfer-Encoding")? .map(|s| s.to_lowercase()); - let decoded = match transfer_coding.unwrap_or_default().as_ref() { - "base64" => { - let cleaned = self.body + + let decoded = match transfer_coding { + Some(ref enc) if enc == "base64" => { + let cleaned = self + .body .iter() - .filter_map(|&c| match c { - b' ' | b'\t' | b'\r' | b'\n' => None, - v => Some(v), - }) + .filter(|c| !c.is_ascii_whitespace()) + .cloned() .collect::>(); - try!(base64::decode(&cleaned)) + base64::decode(&cleaned)? } - "quoted-printable" => { - try!(quoted_printable::decode( - self.body, - quoted_printable::ParseMode::Robust, - )) + Some(ref enc) if enc == "quoted-printable" => { + quoted_printable::decode(self.body, quoted_printable::ParseMode::Robust)? } _ => Vec::::from(self.body), }; @@ -765,7 +751,7 @@ impl<'a> ParsedMail<'a> { /// assert_eq!(dateparse(parsed.headers.get_first_value("Date").unwrap().unwrap().as_str()).unwrap(), 1475417182); /// ``` pub fn parse_mail(raw_data: &[u8]) -> Result { - let (headers, ix_body) = try!(parse_headers(raw_data)); + let (headers, ix_body) = parse_headers(raw_data)?; let ctype = headers .get_first_value("Content-Type")? .map(|s| parse_content_type(&s)) @@ -791,9 +777,7 @@ pub fn parse_mail(raw_data: &[u8]) -> Result { let ix_part_end = find_from_u8(raw_data, ix_part_start, boundary.as_bytes()) .unwrap_or_else(|| raw_data.len()); - result.subparts.push(try!(parse_mail( - &raw_data[ix_part_start..ix_part_end], - ))); + result.subparts.push(parse_mail(&raw_data[ix_part_start..ix_part_end])?); ix_boundary_end = ix_part_end + boundary.len(); if ix_boundary_end + 2 > raw_data.len() || (raw_data[ix_boundary_end] == b'-' && raw_data[ix_boundary_end + 1] == b'-') @@ -826,17 +810,16 @@ fn parse_param_content(content: &str) -> ParamContent { // There must be at least one token produced by split, even if it's empty. let value = tokens.next().unwrap().trim(); let map = tokens - .filter_map(|kv| if let Some(idx) = kv.find('=') { - let key = kv[0..idx].trim().to_lowercase(); - let mut value = kv[idx + 1..].trim(); - if value.starts_with('"') && value.ends_with('"') { - value = &value[1..value.len() - 1]; - } - Some((key, value.to_string())) - } else { - None - }) - .collect(); + .filter_map(|kv| { + kv.find('=').map(|idx| { + let key = kv[0..idx].trim().to_lowercase(); + let mut value = kv[idx + 1..].trim(); + if value.starts_with('"') && value.ends_with('"') { + value = &value[1..value.len() - 1]; + } + (key, value.to_string()) + }) + }).collect(); ParamContent { value: value.into(), diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 980f0eb..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,8 +0,0 @@ -macro_rules! try_none { - ( $x:expr ) => {{ - match $x { - Some(v) => v, - None => return None, - } - }} -}