diff --git a/melib/src/mailbox/email/compose.rs b/melib/src/mailbox/email/compose.rs index e8445c86..14e267be 100644 --- a/melib/src/mailbox/email/compose.rs +++ b/melib/src/mailbox/email/compose.rs @@ -4,6 +4,9 @@ use data_encoding::BASE64_MIME; use std::str; mod random; +mod mime; + +use self::mime::*; use super::parser; @@ -15,6 +18,7 @@ pub struct Draft { // FIXME: Preserve header order // FIXME: Validate headers, allow custom ones headers: FnvHashMap, + header_order: Vec, body: String, attachments: Vec, @@ -23,18 +27,28 @@ pub struct Draft { impl Default for Draft { fn default() -> Self { let mut headers = FnvHashMap::with_capacity_and_hasher(8, Default::default()); + let mut header_order = Vec::with_capacity(8); headers.insert("From".into(), "".into()); headers.insert("To".into(), "".into()); headers.insert("Cc".into(), "".into()); headers.insert("Bcc".into(), "".into()); + header_order.push("From".into()); + header_order.push("To".into()); + header_order.push("Cc".into()); + header_order.push("Bcc".into()); let now: DateTime = Local::now(); headers.insert("Date".into(), now.to_rfc2822()); headers.insert("Subject".into(), "".into()); headers.insert("Message-ID".into(), random::gen_message_id()); headers.insert("User-Agent".into(), "meli".into()); + header_order.push("Date".into()); + header_order.push("Subject".into()); + header_order.push("Message-ID".into()); + header_order.push("User-Agent".into()); Draft { headers, + header_order, body: String::new(), attachments: Vec::new(), @@ -46,7 +60,7 @@ impl str::FromStr for Draft { type Err = MeliError; fn from_str(s: &str) -> Result { if s.is_empty() { - return Err(MeliError::new("sadfsa")); + return Err(MeliError::new("Empty input in Draft::from_str")); } let (headers, _) = parser::mail(s.as_bytes()).to_full_result()?; @@ -56,10 +70,13 @@ impl str::FromStr for Draft { if ignore_header(k) { continue; } - ret.headers.insert( + if ret.headers.insert( String::from_utf8(k.to_vec())?, String::from_utf8(v.to_vec())?, - ); + ).is_none() { + ret.header_order.push(String::from_utf8(k.to_vec())?); + } + } let body = Envelope::new(0).body_bytes(s.as_bytes()); @@ -92,8 +109,10 @@ impl Draft { envelope.message_id_display() ), ); + ret.header_order.push("References".into()); ret.headers_mut() .insert("In-Reply-To".into(), envelope.message_id_display().into()); + ret.header_order.push("In-Reply-To".into()); ret.headers_mut() .insert("To".into(), envelope.field_from_to_string()); ret.headers_mut() @@ -135,16 +154,21 @@ impl Draft { pub fn to_string(&self) -> Result { let mut ret = String::new(); - let headers = &["Date", "From", "To", "Cc", "Bcc", "Subject", "Message-ID"]; - for k in headers { - ret.extend(format!("{}: {}\n", k, &self.headers[*k]).chars()); + for k in &self.header_order { + let v = &self.headers[k]; + ret.extend(format!("{}: {}\n", k, v).chars()); } - for (k, v) in &self.headers { - if headers.contains(&k.as_str()) { - continue; - } + ret.push('\n'); + ret.push_str(&self.body); + + Ok(ret) + } + pub fn finalise(self) -> Result { + let mut ret = String::new(); + for k in &self.header_order { + let v = &self.headers[k]; ret.extend(format!("{}: {}\n", k, v).chars()); } @@ -167,6 +191,7 @@ impl Draft { } Ok(ret) + } } diff --git a/melib/src/mailbox/email/compose/mime.rs b/melib/src/mailbox/email/compose/mime.rs new file mode 100644 index 00000000..4563e55b --- /dev/null +++ b/melib/src/mailbox/email/compose/mime.rs @@ -0,0 +1 @@ +use super::*; diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index 69a1a4e0..e08349e8 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -129,9 +129,22 @@ named!(name<&[u8]>, is_not!(":\n")); /* Parse a single header as a tuple -> (&str, Vec<&str>) */ named!( - header<(&[u8], &[u8])>, + header_has_val<(&[u8], &[u8])>, separated_pair!(complete!(name), ws!(tag!(b":")), complete!(header_value)) ); + +named!( + header_no_val<(&[u8], &[u8])>, + do_parse!( + name: complete!(name) >> + tag!(b":") >> + opt!(is_a!(" \t")) >> + tag!(b"\n") >> + ( { (name, b"") } ))); + +named!( + header<(&[u8], &[u8])>, + alt_complete!(header_no_val | header_has_val)); /* Parse all headers -> Vec<(&str, Vec<&str>)> */ named!(pub headers>, many1!(complete!(header))); diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 9026ea19..6cf7e3d0 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -399,7 +399,15 @@ impl Component for Composer { }); return true; } - ('n', _) => {} + ('n', _) => {}, + ('y', ViewMode::Discard(u)) => { + eprintln!("{:?}", self.draft.to_string()); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::Action(Tab(Kill(*u))), + }); + return true; + }, _ => { return false; }