Skip to content

Tidy Result/Option use #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 44 additions & 61 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ use std::collections::BTreeMap;

use encoding::Encoding;

#[macro_use]
mod macros;
mod dateparse;

pub use dateparse::dateparse;
Expand Down Expand Up @@ -95,11 +93,9 @@ pub struct MailHeader<'a> {
}

fn is_boundary(line: &str, ix: Option<usize>) -> 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<usize> {
Expand Down Expand Up @@ -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<String, MailParseError> {
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<String> {
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
Expand All @@ -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()
Expand All @@ -199,10 +192,7 @@ impl<'a> MailHeader<'a> {
/// ```
pub fn get_value(&self) -> Result<String, MailParseError> {
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) {
Expand Down Expand Up @@ -411,7 +401,7 @@ pub trait MailHeaderMap {
impl<'a> MailHeaderMap for Vec<MailHeader<'a>> {
fn get_first_value(&self, key: &str) -> Result<Option<String>, 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);
}
}
Expand All @@ -421,8 +411,8 @@ impl<'a> MailHeaderMap for Vec<MailHeader<'a>> {
fn get_all_values(&self, key: &str) -> Result<Vec<String>, MailParseError> {
let mut values: Vec<String> = 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)
Expand Down Expand Up @@ -472,7 +462,7 @@ pub fn parse_headers(raw_data: &[u8]) -> Result<(Vec<MailHeader>, 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;
}
Expand Down Expand Up @@ -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<u8>. This function tries to
Expand All @@ -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<Vec<u8>, 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::<Vec<u8>>();
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::<u8>::from(self.body),
};
Expand Down Expand Up @@ -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<ParsedMail, MailParseError> {
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))
Expand All @@ -791,9 +777,7 @@ pub fn parse_mail(raw_data: &[u8]) -> Result<ParsedMail, MailParseError> {
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'-')
Expand Down Expand Up @@ -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(),
Expand Down
8 changes: 0 additions & 8 deletions src/macros.rs

This file was deleted.