summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2018-08-04 20:40:20 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-06-10 19:40:26 +0300
commite91f22cb4f1c2324847e7895d25e6a5446929cd0 (patch)
tree9f0535f686cdd23bca68993709972abe963bd20d
parentd5c0542f61d1e36709aabd97d2321307a3ca023e (diff)
Remove some string conversions in parsing
-rw-r--r--melib/src/mailbox/email/attachments.rs95
-rw-r--r--melib/src/mailbox/email/mod.rs123
-rw-r--r--melib/src/mailbox/email/parser.rs151
-rw-r--r--melib/src/mailbox/thread.rs38
-rw-r--r--ui/src/components/utilities.rs2
5 files changed, 226 insertions, 183 deletions
diff --git a/melib/src/mailbox/email/attachments.rs b/melib/src/mailbox/email/attachments.rs
index 66a2d6aa..639db8c8 100644
--- a/melib/src/mailbox/email/attachments.rs
+++ b/melib/src/mailbox/email/attachments.rs
@@ -19,6 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use mailbox::email::parser;
+use mailbox::email::parser::BytesExt;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str;
@@ -28,7 +29,7 @@ use data_encoding::BASE64_MIME;
/*
*
* Data
- * Text { content: String }
+ * Text { content: Vec<u8> }
* Multipart
*/
@@ -37,7 +38,7 @@ pub enum MultipartType {
Mixed,
Alternative,
Digest,
- Unsupported { tag: String },
+ Unsupported { tag: Vec<u8> },
}
impl Display for MultipartType {
@@ -46,7 +47,7 @@ impl Display for MultipartType {
MultipartType::Mixed => write!(f, "multipart/mixed"),
MultipartType::Alternative => write!(f, "multipart/alternative"),
MultipartType::Digest => write!(f, "multipart/digest"),
- MultipartType::Unsupported { tag: ref t } => write!(f, "multipart/{}", t),
+ MultipartType::Unsupported { tag: ref t } => write!(f, "multipart/{}", String::from_utf8_lossy(t)),
}
}
}
@@ -54,10 +55,10 @@ impl Display for MultipartType {
#[derive(Clone, Debug)]
pub enum AttachmentType {
Data {
- tag: String,
+ tag: Vec<u8>,
},
Text {
- content: String,
+ content: Vec<u8>,
},
Multipart {
of_type: MultipartType,
@@ -68,8 +69,8 @@ pub enum AttachmentType {
impl Display for AttachmentType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match self {
- AttachmentType::Data { tag: ref t } => write!(f, "{}", t),
- AttachmentType::Text { content: ref c } => write!(f, "{}", c),
+ AttachmentType::Data { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
+ AttachmentType::Text { content: ref c } => write!(f, "{}", String::from_utf8_lossy(c)),
AttachmentType::Multipart { of_type: ref t, .. } => write!(f, "{}", t),
}
}
@@ -77,8 +78,8 @@ impl Display for AttachmentType {
#[derive(Clone, Debug)]
pub enum ContentType {
Text,
- Multipart { boundary: String },
- Unsupported { tag: String },
+ Multipart { boundary: Vec<u8> },
+ Unsupported { tag: Vec<u8> },
}
impl Display for ContentType {
@@ -86,20 +87,20 @@ impl Display for ContentType {
match *self {
ContentType::Text => write!(f, "text"),
ContentType::Multipart { .. } => write!(f, "multipart"),
- ContentType::Unsupported { tag: ref t } => write!(f, "{}", t),
+ ContentType::Unsupported { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ContentSubType {
Plain,
- Other { tag: String },
+ Other { tag: Vec<u8> },
}
impl Display for ContentSubType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
ContentSubType::Plain => write!(f, "plain"),
- ContentSubType::Other { tag: ref t } => write!(f, "{}", t),
+ ContentSubType::Other { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)),
}
}
}
@@ -109,7 +110,7 @@ pub enum ContentTransferEncoding {
_7Bit,
Base64,
QuotedPrintable,
- Other { tag: String },
+ Other { tag: Vec<u8> },
}
/// TODO: Add example.
@@ -129,13 +130,17 @@ impl AttachmentBuilder {
raw: content.to_vec(),
}
}
- pub fn content_type(&mut self, value: &str) -> &Self {
- match parser::content_type(value.as_bytes()).to_full_result() {
- Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case("multipart") {
+ pub fn content_type(&mut self, value: &[u8]) -> &Self {
+ match parser::content_type(value).to_full_result() {
+ Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case(b"multipart") {
let mut boundary = None;
for (n, v) in params {
- if n.eq_ignore_ascii_case("boundary") {
- boundary = Some(format!("--{}--", v).to_string());
+ if n.eq_ignore_ascii_case(b"boundary") {
+ let mut vec: Vec<u8> = Vec::with_capacity(v.len()+4);
+ vec.extend_from_slice(b"--");
+ vec.extend(v);
+ vec.extend_from_slice(b"--");
+ boundary = Some(vec);
break;
}
}
@@ -144,11 +149,11 @@ impl AttachmentBuilder {
boundary: boundary.unwrap(),
};
self.content_type.1 = ContentSubType::Other {
- tag: cst.to_string(),
+ tag: cst.into(),
};
- } else if ct.eq_ignore_ascii_case("text") {
+ } else if ct.eq_ignore_ascii_case(b"text") {
self.content_type.0 = ContentType::Text;
- if !cst.eq_ignore_ascii_case("plain") {
+ if !cst.eq_ignore_ascii_case(b"plain") {
self.content_type.1 = ContentSubType::Other {
tag: cst.to_ascii_lowercase(),
};
@@ -167,14 +172,14 @@ impl AttachmentBuilder {
}
self
}
- pub fn content_transfer_encoding(&mut self, value: &str) -> &Self {
- self.content_transfer_encoding = if value.eq_ignore_ascii_case("base64") {
+ pub fn content_transfer_encoding(&mut self, value: &[u8]) -> &Self {
+ self.content_transfer_encoding = if value.eq_ignore_ascii_case(b"base64") {
ContentTransferEncoding::Base64
- } else if value.eq_ignore_ascii_case("7bit") {
+ } else if value.eq_ignore_ascii_case(b"7bit") {
ContentTransferEncoding::_7Bit
- } else if value.eq_ignore_ascii_case("8bit") {
+ } else if value.eq_ignore_ascii_case(b"8bit") {
ContentTransferEncoding::_8Bit
- } else if value.eq_ignore_ascii_case("quoted-printable") {
+ } else if value.eq_ignore_ascii_case(b"quoted-printable") {
ContentTransferEncoding::QuotedPrintable
} else {
ContentTransferEncoding::Other {
@@ -183,7 +188,7 @@ impl AttachmentBuilder {
};
self
}
- fn decode(&self) -> String {
+ fn decode(&self) -> Vec<u8> {
// TODO: Use charset for decoding
match self.content_transfer_encoding {
ContentTransferEncoding::Base64 => match BASE64_MIME.decode(
@@ -197,15 +202,17 @@ impl AttachmentBuilder {
})
.as_bytes(),
) {
- Ok(ref v) => {
- let s = String::from_utf8_lossy(v);
- if s.find("\r\n").is_some() {
- s.replace("\r\n", "\n")
- } else {
- s.into_owned()
+ Ok(ref s) => {
+ let s:Vec<u8> = s.clone();
+ {
+ let slice = &s[..];
+ if slice.find(b"\r\n").is_some() {
+ s.replace(b"\r\n", b"\n");
}
+ }
+ s
}
- _ => String::from_utf8_lossy(&self.raw).into_owned(),
+ _ => self.raw.clone()
},
ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw)
.to_full_result()
@@ -213,7 +220,7 @@ impl AttachmentBuilder {
ContentTransferEncoding::_7Bit
| ContentTransferEncoding::_8Bit
| ContentTransferEncoding::Other { .. } => {
- String::from_utf8_lossy(&self.raw).into_owned()
+ self.raw.clone()
}
}
}
@@ -224,11 +231,11 @@ impl AttachmentBuilder {
},
ContentType::Multipart { boundary: ref b } => {
let multipart_type = match self.content_type.1 {
- ContentSubType::Other { ref tag } => match tag.as_ref() {
- "mixed" => MultipartType::Mixed,
- "alternative" => MultipartType::Alternative,
- "digest" => MultipartType::Digest,
- t => MultipartType::Unsupported { tag: t.to_string() },
+ ContentSubType::Other { ref tag } => match &tag[..] {
+ b"mixed" => MultipartType::Mixed,
+ b"alternative" => MultipartType::Alternative,
+ b"digest" => MultipartType::Digest,
+ _ => MultipartType::Unsupported { tag:tag.clone() },
},
_ => panic!(),
};
@@ -247,7 +254,7 @@ impl AttachmentBuilder {
}
}
- pub fn subattachments(raw: &[u8], boundary: &str) -> Vec<Attachment> {
+ pub fn subattachments(raw: &[u8], boundary: &[u8]) -> Vec<Attachment> {
let boundary_length = boundary.len();
match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result()
{
@@ -267,9 +274,9 @@ impl AttachmentBuilder {
};
let mut builder = AttachmentBuilder::new(body);
for (name, value) in headers {
- if name.eq_ignore_ascii_case("content-type") {
+ if name.eq_ignore_ascii_case(b"content-type") {
builder.content_type(value);
- } else if name.eq_ignore_ascii_case("content-transfer-encoding") {
+ } else if name.eq_ignore_ascii_case(b"content-transfer-encoding") {
builder.content_transfer_encoding(value);
}
}
@@ -333,7 +340,7 @@ impl Attachment {
//text.push_str(&format!("Data attachment of type {}", self.mime_type()));
}
AttachmentType::Text { content: ref t } => {
- text.push_str(t);
+ text.push_str(&String::from_utf8_lossy(t));
}
AttachmentType::Multipart {
of_type: ref multipart_type,
diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs
index 0a21ddfa..fe9eced3 100644
--- a/melib/src/mailbox/email/mod.rs
+++ b/melib/src/mailbox/email/mod.rs
@@ -22,6 +22,7 @@
pub mod attachments;
pub mod parser;
+use parser::BytesExt;
pub use self::attachments::*;
use error::{MeliError, Result};
use mailbox::backends::BackendOpGenerator;
@@ -31,20 +32,21 @@ use std::fmt;
use std::option::Option;
use std::string::String;
use std::sync::Arc;
+use std::borrow::Cow;
use chrono;
use chrono::TimeZone;
#[derive(Clone, Debug)]
pub struct GroupAddress {
- raw: String,
+ raw: Vec<u8>,
display_name: StrBuilder,
mailbox_list: Vec<Address>,
}
#[derive(Clone, Debug)]
pub struct MailboxAddress {
- raw: String,
+ raw: Vec<u8>,
display_name: StrBuilder,
address_spec: StrBuilder,
}
@@ -110,42 +112,42 @@ struct StrBuilder {
/// Structs implementing this trait must contain a `StrBuilder` field.
pub trait StrBuild {
/// Create a new `Self` out of a string and a slice
- fn new(string: &str, slice: &str) -> Self;
+ fn new(string: &[u8], slice: &[u8]) -> Self;
/// Get the slice part of the string
- fn raw(&self) -> &str;
+ fn raw(&self) -> &[u8];
/// Get the entire string as a slice
- fn val(&self) -> &str;
+ fn val(&self) -> &[u8];
}
impl StrBuilder {
- fn display<'a>(&self, s: &'a str) -> &'a str {
+ fn display<'a>(&self, s: &'a [u8]) -> String {
let offset = self.offset;
let length = self.length;
- &s[offset..offset + length]
+ String::from_utf8(s[offset..offset + length].to_vec()).unwrap()
}
}
/// `MessageID` is accessed through the `StrBuild` trait.
#[derive(Clone)]
-pub struct MessageID(String, StrBuilder);
+pub struct MessageID(Vec<u8>, StrBuilder);
impl StrBuild for MessageID {
- fn new(string: &str, slice: &str) -> Self {
+ fn new(string: &[u8], slice: &[u8]) -> Self {
let offset = string.find(slice).unwrap();
MessageID(
- string.to_string(),
+ string.to_owned(),
StrBuilder {
offset: offset,
length: slice.len() + 1,
},
)
}
- fn raw(&self) -> &str {
+ fn raw(&self) -> &[u8] {
let offset = self.1.offset;
let length = self.1.length;
&self.0[offset..offset + length - 1]
}
- fn val(&self) -> &str {
+ fn val(&self) -> &[u8] {
&self.0
}
}
@@ -173,13 +175,13 @@ impl PartialEq for MessageID {
}
impl fmt::Debug for MessageID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.raw())
+ write!(f, "{}", String::from_utf8(self.raw().to_vec()).unwrap())
}
}
#[derive(Clone, Debug)]
struct References {
- raw: String,
+ raw: Vec<u8>,
refs: Vec<MessageID>,
}
@@ -208,7 +210,7 @@ pub struct Envelope {
from: Vec<Address>,
to: Vec<Address>,
body: Option<Attachment>,
- subject: Option<String>,
+ subject: Option<Vec<u8>>,
message_id: Option<MessageID>,
in_reply_to: Option<MessageID>,
references: Option<References>,
@@ -268,48 +270,48 @@ impl Envelope {
if value.len() == 1 && value.is_empty() {
continue;
}
- if name.eq_ignore_ascii_case("to") {
- let parse_result = parser::rfc2822address_list(value.as_bytes());
+ if name.eq_ignore_ascii_case(b"to") {
+ let parse_result = parser::rfc2822address_list(value);
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
Vec::new()
};
self.set_to(value);
- } else if name.eq_ignore_ascii_case("from") {
- let parse_result = parser::rfc2822address_list(value.as_bytes());
+ } else if name.eq_ignore_ascii_case(b"from") {
+ let parse_result = parser::rfc2822address_list(value);
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
Vec::new()
};
self.set_from(value);
- } else if name.eq_ignore_ascii_case("subject") {
- let parse_result = parser::phrase(value.trim().as_bytes());
+ } else if name.eq_ignore_ascii_case(b"subject") {
+ let parse_result = parser::phrase(value.trim());
let value = if parse_result.is_done() {
parse_result.to_full_result().unwrap()
} else {
- "".to_string()
+ "".into()
};
self.set_subject(value);
- } else if name.eq_ignore_ascii_case("message-id") {
+ } else if name.eq_ignore_ascii_case(b"message-id") {
self.set_message_id(value);
- } else if name.eq_ignore_ascii_case("references") {
+ } else if name.eq_ignore_ascii_case(b"references") {
{
- let parse_result = parser::references(value.as_bytes());
+ let parse_result = parser::references(value);
if parse_result.is_done() {
for v in parse_result.to_full_result().unwrap() {
self.push_references(v);
}
}
}
- self.set_references(value.to_string());
- } else if name.eq_ignore_ascii_case("in-reply-to") {
+ self.set_references(value);
+ } else if name.eq_ignore_ascii_case(b"in-reply-to") {
self.set_in_reply_to(value);
in_reply_to = Some(value);
- } else if name.eq_ignore_ascii_case("date") {
- self.set_date(value.to_string());
- datetime = Some(value.to_string());
+ } else if name.eq_ignore_ascii_case(b"date") {
+ self.set_date(value);
+ datetime = Some(value);
}
}
/*
@@ -374,46 +376,46 @@ impl Envelope {
if value.len() == 1 && value.is_empty() {
continue;
}
- if name.eq_ignore_ascii_case("content-transfer-encoding") {
+ if name.eq_ignore_ascii_case(b"content-transfer-encoding") {
builder.content_transfer_encoding(value);
- } else if name.eq_ignore_ascii_case("content-type") {
+ } else if name.eq_ignore_ascii_case(b"content-type") {
builder.content_type(value);
}
}
builder.build()
}
- pub fn subject(&self) -> &str {
+ pub fn subject(&self) -> Cow<str> {
match self.subject {
- Some(ref s) => s,
- _ => "",
+ Some(ref s) => String::from_utf8_lossy(s),
+ _ => Cow::from(String::new()),
}
}
- pub fn in_reply_to(&self) -> &str {
+ pub fn in_reply_to(&self) -> Cow<str> {
match self.in_reply_to {
- Some(ref s) => s.val(),
- _ => "",
+ Some(ref s) => String::from_utf8_lossy(s.val()),
+ _ => Cow::from(String::new()),
}
}
- pub fn in_reply_to_raw(&self) -> &str {
+ pub fn in_reply_to_raw(&self) -> Cow<str> {
match self.in_reply_to {
- Some(ref s) => s.raw(),
- _ => "",
+ Some(ref s) => String::from_utf8_lossy(s.raw()).into(),
+ _ => Cow::from(String::new()),
}
}
- pub fn message_id(&self) -> &str {
+ pub fn message_id(&self) -> Cow<str> {
match self.message_id {
- Some(ref s) => s.val(),
- _ => "",
+ Some(ref s) => String::from_utf8_lossy(s.val()),
+ _ => Cow::from(String::new()),
}
}
- pub fn message_id_raw(&self) -> &str {
+ pub fn message_id_raw(&self) -> Cow<str> {
match self.message_id {
- Some(ref s) => s.raw(),
- _ => "",
+ Some(ref s) => String::from_utf8_lossy(s.raw()),
+ _ => Cow::from(String::new()),
}
}
- fn set_date(&mut self, new_val: String) -> () {
- self.date = new_val;
+ fn set_date(&mut self, new_val: &[u8]) -> () {
+ self.date = String::from_utf8_lossy(new_val).into_owned();
}
fn set_from(&mut self, new_val: Vec<Address>) -> () {
self.from = new_val;
@@ -421,8 +423,8 @@ impl Envelope {
fn set_to(&mut self, new_val: Vec<Address>) -> () {
self.to = new_val;
}
- fn set_in_reply_to(&mut self, new_val: &str) -> () {
- let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
+ fn set_in_reply_to(&mut self, new_val: &[u8]) -> () {
+ let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
self.in_reply_to = None;
@@ -431,11 +433,11 @@ impl Envelope {
};
self.in_reply_to = Some(MessageID::new(new_val, slice));
}
- fn set_subject(&mut self, new_val: String) -> () {
+ fn set_subject(&mut self, new_val: Vec<u8>) -> () {
self.subject = Some(new_val);
}
- fn set_message_id(&mut self, new_val: &str) -> () {
- let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
+ fn set_message_id(&mut self, new_val: &[u8]) -> () {
+ let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
self.message_id = None;
@@ -444,8 +446,8 @@ impl Envelope {
};
self.message_id = Some(MessageID::new(new_val, slice));
}
- fn push_references(&mut self, new_val: &str) -> () {
- let slice = match parser::message_id(new_val.as_bytes()).to_full_result() {
+ fn push_references(&mut self, new_val: &[u8]) -> () {
+ let slice = match parser::message_id(new_val).to_full_result() {
Ok(v) => v,
Err(_) => {
return;
@@ -471,21 +473,22 @@ impl Envelope {
let mut v = Vec::new();
v.push(new_ref);
self.references = Some(References {
- raw: "".to_string(),
+ raw: "".into(),
refs: v,
});
}
}
}
- fn set_references(&mut self, new_val: String) -> () {
+ // TODO: Check what references should be like again.
+ fn set_references(&mut self, new_val: &[u8]) -> () {
match self.references {
Some(ref mut s) => {
- s.raw = new_val;
+ s.raw = new_val.into();
}
None => {
let v = Vec::new();
self.references = Some(References {
- raw: new_val,
+ raw: new_val.into(),
refs: v,
});
}
diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs
index 80f7ff30..08baa6ed 100644
--- a/melib/src/mailbox/email/parser.rs
+++ b/melib/src/mailbox/email/parser.rs
@@ -28,6 +28,45 @@ use nom::{ErrorKind, IResult, Needed};
use std;
use std::str::from_utf8;
+pub trait BytesExt {
+ fn trim(&self) -> &Self;
+ fn find(&self, needle: &[u8]) -> Option<usize>;
+ fn replace(&self, from: &[u8], to: &[u8]) -> Vec<u8>;
+}
+
+impl BytesExt for [u8] {
+ fn trim(&self) -> &[u8] {
+ fn is_whitespace(c: &u8) -> bool {
+ *c == b'\t' || *c == b' '
+ }
+
+ fn is_not_whitespace(c: &u8) -> bool {
+ !is_whitespace(c)
+ }
+
+ if let Some(first) = self.iter().position(is_not_whitespace) {
+ if let Some(last) = self.iter().rposition(is_not_whitespace) {
+ &self[first..last + 1]
+ } else {
+ unreachable!();
+ }
+ } else {
+ &[]
+ }
+ }
+ // https://stackoverflow.com/a/35907071
+ fn find(&self, needle: &[u8]) -> Option<usize> {
+ self.windows(needle.len()).position(|window| window == needle)
+ }
+ fn replace(&self, from: &[u8], to: &[u8]) -> Vec<u8> {
+ let mut ret = self.to_vec();
+ if let Some(idx) = self.find(from) {
+ ret.splice(idx..(idx + from.len()), to.iter().cloned());
+ }
+ ret
+ }
+}
+
macro_rules! is_whitespace {
($var:ident) => {
$var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r'
@@ -71,7 +110,7 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> {
* Tue, 5 Jan 2016 21:30:44 +0100 (CET)
*/
-fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
+fn header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
if input.is_empty() || input[0] == b'\n' {
IResult::Incomplete(Needed::Unknown)
} else {
@@ -79,15 +118,9 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
for (i, x) in input.iter().enumerate() {
if *x == b'\n' {
if (i + 1) < input_len && input[i + 1] != b' ' && input[i + 1] != b'\t' {
- return match from_utf8(&input[0..i]) {
- Ok(v) => IResult::Done(&input[(i + 1)..], v),
- Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))),
- };
+ return IResult::Done(&input[(i + 1)..], &input[0..i]);
} else if i + 1 == input_len {
- return match from_utf8(input) {
- Ok(v) => IResult::Done(&input[(i + 1)..], v),
- Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))),
- };
+ return IResult::Done(&input[(i + 1)..], &input[0..i]);
}
}
}
@@ -96,15 +129,15 @@ fn header_value(input: &[u8]) -> IResult<&[u8], &str> {
}
/* Parse the name part of the header -> &str */
-named!(name<&str>, map_res!(is_not!(":\n"), from_utf8));
+named!(name<&[u8]>, is_not!(":\n"));
/* Parse a single header as a tuple -> (&str, Vec<&str>) */
named!(
- header<(&str, &str)>,
- separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value))
+ header<(&[u8], &[u8])>,
+ separated_pair!(complete!(name), ws!(tag!(b":")), complete!(header_value))
);
/* Parse all headers -> Vec<(&str, Vec<&str>)> */
-named!(pub headers<std::vec::Vec<(&str, &str)>>,
+named!(pub headers<std::vec::Vec<(&[u8], &[u8])>>,
many1!(complete!(header)));
//named!(pub headers_raw<&[u8]>,
@@ -128,9 +161,9 @@ named!(pub body_raw<&[u8]>,
body: take_while!(call!(|_| true)) >>
( { body } )));
-named!(pub mail<(std::vec::Vec<(&str, &str)>, &[u8])>,
- separated_pair!(headers, tag!("\n"), take_while!(call!(|_| true))));
-named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>,
+named!(pub mail<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
+ separated_pair!(headers, tag!(b"\n"), take_while!(call!(|_| true))));
+named!(pub attachment<(std::vec::Vec<(&[u8], &[u8])>, &[u8])>,
do_parse!(
opt!(is_a!(" \n\t\r")) >>
pair: pair!(many0!(complete!(header)), take_while!(call!(|_| true))) >>
@@ -252,7 +285,7 @@ named!(
);
named!(
- encoded_word_list<String>,
+ encoded_word_list<Vec<u8>>,
ws!(do_parse!(
list: separated_nonempty_list!(complete!(is_a!(" \n\r\t")), encoded_word) >> ({
let list_len = list.iter().fold(0, |mut acc, x| {
@@ -264,17 +297,17 @@ named!(
acc.append(&mut x.clone());
acc
});
- String::from_utf8_lossy(&bytes).into_owned()
+ bytes
})
))
);
named!(
- ascii_token<String>,
+ ascii_token<Vec<u8>>,
do_parse!(
word: alt!(
terminated!(take_until1!("=?"), peek!(tag_no_case!("=?UTF-8?")))
| take_while!(call!(|_| true))
- ) >> ({ String::from_utf8_lossy(word).into_owned() })
+ ) >> ({ word.into() })
)
);
@@ -317,7 +350,7 @@ fn display_addr(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Error(e) => IResult::Error(e),
IResult::Incomplete(i) => IResult::Incomplete(i),
IResult::Done(rest, raw) => {
- display_name.length = raw.find('<').unwrap();
+ display_name.length = raw.find(b"<").unwrap();
address_spec.offset = display_name.length + 1;
address_spec.length = raw.len() - display_name.length - 2;
IResult::Done(
@@ -357,7 +390,7 @@ fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> {
IResult::Done(
&input[end..],
Address::Mailbox(MailboxAddress {
- raw: String::from_utf8_lossy(&input[0..end + 1]).to_string(),
+ raw: input[0..end + 1].into(),
display_name: StrBuilder {
offset: 0,
length: 0,
@@ -446,7 +479,7 @@ fn group(input: &[u8]) -> IResult<&[u8], Address> {
return IResult::Done(
rest,
Address::Group(GroupAddress {
- raw: String::from_utf8(input[0..size].to_vec()).unwrap(),
+ raw: input[0..size].into(),
display_name: StrBuilder {
offset: 0,
length: dlength,
@@ -480,13 +513,13 @@ named!(pub rfc2822address_list<Vec<Address>>, ws!(
named!(pub address_list<String>, ws!(do_parse!(
list: alt_complete!( encoded_word_list | ascii_token) >>
( {
- let list: Vec<&str> = list.split(',').collect();
+ let list: Vec<&[u8]> = list.split(|c| *c == b',').collect();
let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.trim().len(); acc }) + list.len() - 1;
let list_len = list.len();
let mut i = 0;
list.iter().fold(String::with_capacity(string_len),
|acc, x| {
- let mut acc = acc + &x.replace("\n", "").replace("\t", " ").trim();
+ let mut acc = acc + &String::from_utf8_lossy(x.replace(b"\n", b"").replace(b"\t", b" ").trim());
if i != list_len - 1 {
acc.push_str(" ");
i+=1;
@@ -497,20 +530,20 @@ named!(pub address_list<String>, ws!(do_parse!(
)));
-named!(pub phrase<String>, ws!(do_parse!(
+named!(pub phrase<Vec<u8>>, ws!(do_parse!(
list: many0!(alt_complete!( encoded_word_list | ascii_token)) >>
( {
if list.len() == 0 {
- String::new()
+ Vec::new()
} else {
let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc }) + list.len() - 1;
let list_len = list.len();
let mut i = 0;
- list.iter().fold(String::with_capacity(string_len),
- |acc, x| {
- let mut acc = acc + &x.replace("\n", "").replace("\t", " ");
+ list.iter().fold(Vec::with_capacity(string_len),
+ |mut acc, x| {
+ acc.extend(x.replace(b"\n", b"").replace(b"\t", b" "));
if i != list_len - 1 {
- acc.push_str(" ");
+ acc.push(b' ');
i+=1;
}
acc
@@ -578,21 +611,21 @@ fn test_phrase() {
phrase(phrase_s).unwrap()
);
}
-fn eat_comments(input: &str) -> String {
+fn eat_comments(input: &[u8]) -> Vec<u8> {
let mut in_comment = false;
input
- .chars()
- .fold(String::with_capacity(input.len()), |mut acc, x| {
- if x == '(' && !in_comment {
+ .iter()
+ .fold(Vec::with_capacity(input.len()), |mut acc, x| {
+ if *x == b'(' && !in_comment {
in_comment = true;
acc
- } else if x == ')' && in_comment {
+ } else if *x == b')' && in_comment {
in_comment = false;
acc
} else if in_comment {
acc
} else {
- acc.push(x);
+ acc.push(*x);
acc
}
})
@@ -610,12 +643,12 @@ fn test_eat_comments() {
* right now we expect input will have no extra spaces in between tokens
*
* We should use a custom parser here*/
-pub fn date(input: &str) -> Option<chrono::DateTime<chrono::FixedOffset>> {
- let parsed_result = phrase(eat_comments(input).as_bytes())
+pub fn date(input: &[u8]) -> Option<chrono::DateTime<chrono::FixedOffset>> {
+ let parsed_result = phrase(&eat_comments(input))
.to_full_result()
.unwrap()
- .replace("-", "+");
- chrono::DateTime::parse_from_rfc2822(parsed_result.trim()).ok()
+ .replace(b"-",b"+");
+ chrono::DateTime::parse_from_rfc2822(String::from_utf8_lossy(parsed_result.trim()).as_ref()).ok()
}
#[test]
@@ -627,11 +660,11 @@ fn test_date() {
assert_eq!(date(_s).unwrap(), date(__s).unwrap());
}
-named!(pub message_id<&str>,
- map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8)
+named!(pub message_id<&[u8]>,
+ complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">")))
);
-fn message_id_peek(input: &[u8]) -> IResult<&[u8], &str> {
+fn message_id_peek(input: &[u8]) -> IResult<&[u8], &[u8]> {
let input_length = input.len();
if input.is_empty() {
IResult::Incomplete(Needed::Size(1))
@@ -640,16 +673,16 @@ fn message_id_peek(input: &[u8]) -> IResult<&[u8], &str> {
} else {
for (i, &x) in input.iter().take(input_length).enumerate().skip(1) {
if x == b'>' {
- return IResult::Done(&input[i + 1..], from_utf8(&input[0..i + 1]).unwrap());
+ return IResult::Done(&input[i + 1..], &input[0..i + 1]);
}
}
IResult::Incomplete(Needed::Unknown)
}
}
-named!(pub references<Vec<&str>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
+named!(pub references<Vec<&[u8]>>, separated_list!(complete!(is_a!(" \n\t\r")), message_id_peek));
-named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
+named_args!(pub attachments<'a>(boundary: &'a [u8], boundary_end: &'a [u8]) < Vec<&'this_is_probably_unique_i_hope_please [u8]> >,
alt_complete!(do_parse!(
take_until!(boundary) >>
vecs: many0!(complete!(do_parse!(
@@ -688,31 +721,27 @@ fn test_attachments() {
}
named!(
- content_type_parameter<(&str, &str)>,
+ content_type_parameter<(&[u8], &[u8])>,
do_parse!(
- tag!(";") >> name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("="))
- >> value:
- map_res!(
- ws!(alt_complete!(
- delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";")
- )),
- from_utf8
- ) >> ({ (name, value) })
+ tag!(";") >>
+ name: terminated!(ws!(take_until!("=")) , tag!("=")) >>
+ value: ws!(alt_complete!( delimited!(tag!("\""), take_until!("\""), tag!("\"")) | is_not!(";"))) >>
+ ({ (name, value) })
)
);
-named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >,
+named!(pub content_type< (&[u8], &[u8], Vec<(&[u8], &[u8])>) >,
do_parse!(
- _type: map_res!(take_until!("/"), from_utf8) >>
+ _type: take_until!("/") >>
tag!("/") >>
- _subtype: map_res!(is_not!(";"), from_utf8) >>
+ _subtype: is_not!(";") >>
parameters: many0!(complete!(content_type_parameter)) >>
( {
(_type, _subtype, parameters)
} )
));
-named!(pub quoted_printable_text<String>,
+named!(pub quoted_printable_text<Vec<u8>>,
do_parse!(
bytes: many0!(alt_complete!(
preceded!(tag!("=\n"), quoted_printable_byte) |
@@ -720,7 +749,7 @@ named!(pub quoted_printable_text<String>,
quoted_printable_byte |
le_u8)) >>
( {
- String::from_utf8_lossy(&bytes).into_owned()
+ bytes
} )
)
);
diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs
index f8b0344e..2803886d 100644
--- a/