diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs index 2c812d401..bcc0d8e76 100644 --- a/melib/src/mailbox/email/mod.rs +++ b/melib/src/mailbox/email/mod.rs @@ -27,7 +27,6 @@ pub use self::attachments::*; use std::string::String; use std::sync::Arc; -//use std; use std::cmp::Ordering; use std::fmt; use std::option::Option; @@ -35,6 +34,69 @@ use std::option::Option; use chrono; use chrono::TimeZone; + + +#[derive(Clone, Debug, )] +pub struct GroupAddress { + raw: String, + display_name: StrBuilder, + mailbox_list: Vec
, +} + +#[derive(Clone, Debug, )] +pub struct MailboxAddress { + raw: String, + display_name: StrBuilder, + address_spec: StrBuilder, +} + +#[derive(Clone, Debug, )] +pub enum Address { + Mailbox(MailboxAddress), + Group(GroupAddress), +} + +impl Eq for Address {} +impl PartialEq for Address { + fn eq(&self, other: &Address) -> bool { + match (self, other) { + (Address::Mailbox(_), Address::Group(_)) | + (Address::Group(_), Address::Mailbox(_)) => { + false + }, + (Address::Mailbox(s), Address::Mailbox(o)) => { + s.address_spec.display(&s.raw) == o.address_spec.display(&o.raw) + }, + (Address::Group(s), Address::Group(o)) => { + s.display_name.display(&s.raw) == o.display_name.display(&o.raw) && + s.mailbox_list.iter().zip(o.mailbox_list.iter()).fold(true, |b, (s, o)| b && (s == o)) + }, + } + } +} + +impl fmt::Display for Address { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Address::Mailbox(m) if m.display_name.length > 0 => { + write!(f, "{} <{}>", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)) + }, + Address::Group(g) => { + let attachment_strings: Vec = g.mailbox_list.iter().map(|a| format!("{}", a)).collect(); + write!(f, "{}: {}", g.display_name.display(&g.raw), attachment_strings.join(", ")) + }, + Address::Mailbox(m) => { + write!(f, "{}", m.address_spec.display(&m.raw)) + }, + } + + } + + +} + + + /// Helper struct to return slices from a struct field on demand. #[derive(Clone, Debug)] struct StrBuilder { @@ -52,6 +114,14 @@ pub trait StrBuild { fn val(&self) -> &str; } +impl StrBuilder { + fn display<'a>(&self, s: &'a str) -> &'a str { + let offset = self.offset; + let length = self.length; + &s[offset..offset+length] + } +} + /// `MessageID` is accessed through the `StrBuild` trait. #[derive(Clone)] pub struct MessageID(String, StrBuilder); @@ -63,14 +133,14 @@ impl StrBuild for MessageID { string.to_string(), StrBuilder { offset: offset, - length: slice.len() + 1, + length: slice.len()+ 1, }, ) } fn raw(&self) -> &str { let offset = self.1.offset; let length = self.1.length; - &self.0[offset..length] + &self.0[offset..offset+length-1] } fn val(&self) -> &str { &self.0 @@ -133,8 +203,8 @@ bitflags! { #[derive(Debug, Clone)] pub struct Envelope { date: String, - from: Option, - to: Option, + from: Vec
, + to: Vec
, body: Option, subject: Option, message_id: Option, @@ -155,8 +225,8 @@ impl Envelope { pub fn new(token: Box) -> Self { Envelope { date: "".to_string(), - from: None, - to: None, + from: Vec::new(), + to: Vec::new(), body: None, subject: None, message_id: None, @@ -198,23 +268,23 @@ impl Envelope { continue; } if name.eq_ignore_ascii_case("to") { - let parse_result = parser::address_list(value.as_bytes()); + let parse_result = parser::rfc2822address_list(value.as_bytes()); let value = if parse_result.is_done() { parse_result.to_full_result().unwrap() } else { - "".to_string() + Vec::new() }; self.set_to(value); } else if name.eq_ignore_ascii_case("from") { - let parse_result = parser::address_list(value.as_bytes()); + let parse_result = parser::rfc2822address_list(value.as_bytes()); let value = if parse_result.is_done() { parse_result.to_full_result().unwrap() } else { - "".to_string() + Vec::new() }; self.set_from(value); } else if name.eq_ignore_ascii_case("subject") { - let parse_result = parser::subject(value.trim().as_bytes()); + let parse_result = parser::phrase(value.trim().as_bytes()); let value = if parse_result.is_done() { parse_result.to_full_result().unwrap() } else { @@ -269,17 +339,20 @@ impl Envelope { pub fn date_as_str(&self) -> &str { &self.date } - pub fn from(&self) -> &str { - match self.from { - Some(ref s) => s, - None => "", - } + pub fn from(&self) -> &Vec
{ + &self.from } - pub fn to(&self) -> &str { - match self.to { - Some(ref s) => s, - None => "", - } + + pub fn from_to_string(&self) -> String { + let _strings: Vec = self.from.iter().map(|a| format!("{}", a)).collect(); + _strings.join(", ") + } + pub fn to(&self) -> &Vec
{ + &self.to + } + pub fn to_to_string(&self) -> String { + let _strings: Vec = self.to.iter().map(|a| format!("{}", a)).collect(); + _strings.join(", ") } pub fn body(&self) -> Attachment { let mut operation = self.operation_token.generate(); @@ -338,11 +411,11 @@ impl Envelope { fn set_date(&mut self, new_val: String) -> () { self.date = new_val; } - fn set_from(&mut self, new_val: String) -> () { - self.from = Some(new_val); + fn set_from(&mut self, new_val: Vec
) -> () { + self.from = new_val; } - fn set_to(&mut self, new_val: String) -> () { - self.to = Some(new_val); + fn set_to(&mut self, new_val: Vec
) -> () { + 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() { diff --git a/melib/src/mailbox/email/parser.rs b/melib/src/mailbox/email/parser.rs index 330fcc202..b8fa3951e 100644 --- a/melib/src/mailbox/email/parser.rs +++ b/melib/src/mailbox/email/parser.rs @@ -26,6 +26,16 @@ use nom::{is_hex_digit, le_u8}; use nom::{ErrorKind, IResult, Needed}; use nom::{Compare, CompareResult}; use encoding::{DecoderTrap, Encoding}; +use super::*; + +macro_rules! is_whitespace { + ($var:ident) => ( + $var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r' + ); + ($var:expr) => ( + $var == b' ' && $var == b'\t' && $var == b'\n' && $var == b'\r' + ); +} fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> { if input.is_empty() || input.len() < 3 { @@ -259,6 +269,205 @@ named!(ascii_token, do_parse!( String::from_utf8_lossy(word).into_owned() } ))); +fn display_addr(input: &[u8]) -> IResult<&[u8], Address> { + if input.is_empty() || input.len() < 3 { + IResult::Incomplete(Needed::Size(1)) + } else if !is_whitespace!(input[0]) { + let mut display_name = StrBuilder { + offset: 0, + length: 0, + }; + let mut flag = false; + for (i, b) in input[0..].iter().enumerate() { + if *b == b'<' { + + display_name.length = if i != 0 { i-1 } else { 0 }; + flag = true; + break; + } + } + if !flag { + return IResult::Error(error_code!(ErrorKind::Custom(43))); + } + let mut end = input.len(); + let mut flag = false; + for (i, b) in input[display_name.length+2..].iter().enumerate() { + if *b == b'@' { + flag = true; + } + if *b == b'>' { + end = i; + break; + } + } + if flag { + let mut address_spec = StrBuilder { + offset: display_name.length + 2, + length: end, + }; + match phrase(&input[0..end+display_name.length+3]) { + IResult::Error(e) => IResult::Error(e), + IResult::Incomplete(i) => IResult::Incomplete(i), + IResult::Done(rest, raw) => { + display_name.length = raw.find('<').unwrap(); + address_spec.offset = display_name.length + 1; + address_spec.length = raw.len() - display_name.length - 2; + IResult::Done(rest, Address::Mailbox(MailboxAddress { + raw: raw, + display_name: display_name, + address_spec: address_spec, + })) + }, + } + } else { + IResult::Error(error_code!(ErrorKind::Custom(43))) + } + } else { + IResult::Error(error_code!(ErrorKind::Custom(43))) + } + +} + + + +fn addr_spec(input: &[u8]) -> IResult<&[u8], Address> { + if input.is_empty() || input.len() < 3 { + IResult::Incomplete(Needed::Size(1)) + } else if !is_whitespace!(input[0]) { + let mut end = input[1..].len(); + let mut flag = false; + for (i, b) in input[1..].iter().enumerate() { + if *b == b'@' { + flag = true; + } + if is_whitespace!(*b) { + end = i; + break; + } + } + if flag { + IResult::Done(&input[end..], Address::Mailbox(MailboxAddress { + raw: String::from_utf8_lossy(&input[0..end+1]).to_string(), + display_name: StrBuilder { + offset: 0, + length: 0, + }, + address_spec: StrBuilder { + offset: 0, + length: input[0..end+1].len(), + }, + })) + } else { + IResult::Error(error_code!(ErrorKind::Custom(43))) + } + } else { + IResult::Error(error_code!(ErrorKind::Custom(42))) + } +} + + +named!(mailbox
, ws!(alt_complete!( + display_addr | + addr_spec + ))); +named!(mailbox_list>, many0!(mailbox)); + + +#[test] +fn test_mailbox() { + { + let s = b"epilys@postretch"; + let r = mailbox(s).unwrap().1; + match r { + Address::Mailbox(ref m) => { + println!("----\n`{}`, `{}`\n----", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)); + }, + _ => {}, + } + } + let s = b"Manos "; + eprintln!("{:?}", display_addr(s).unwrap()); + let r = display_addr(s).unwrap().1; + match r { + Address::Mailbox(ref m) => { + println!("----\n`{}`, `{}`\n----", m.display_name.display(&m.raw), m.address_spec.display(&m.raw)); + }, + _ => {}, + } + +} + + +//named!(group_t, ws!( do_parse!( +// display_name: take_until1!(":") >> +// mailbox_list: many0!(mailbox) >> +// end: is_a!(";") >> +// ({ +// +// }) +// ))); +// + +fn group(input: &[u8]) -> IResult<&[u8], Address> { + let mut flag = false; + let mut dlength = 0; + for (i, b) in input.iter().enumerate() { + if *b == b':' { + flag = true; + dlength = i; + break; + } + } + if !flag { + return IResult::Error(error_code!(ErrorKind::Custom(43))); + } + + match mailbox_list(&input[dlength..]) { + IResult::Error(e) => { + return IResult::Error(e); + } + IResult::Done(rest, vec) => { + let size: usize = (rest.as_ptr() as usize) - ((&input[0..] as &[u8]).as_ptr() as usize); + return IResult::Done(rest, Address::Group(GroupAddress { + raw: String::from_utf8(input[0..size].to_vec()).unwrap(), + display_name: StrBuilder { + offset: 0, + length: dlength, + }, + mailbox_list: vec, + })); + }, + IResult::Incomplete(i) => { + return IResult::Incomplete(i); + }, + } +} + + + + + +named!(address
, ws!( + alt_complete!(mailbox | group))); + +#[test] +fn test_address() { + let s = b"Manos Pitsidianakis , + qemu-devel , qemu-block , + Alberto Garcia , Stefan Hajnoczi "; + println!("{:?}", rfc2822address_list(s).unwrap()); + +} + + + +named!(pub rfc2822address_list>, ws!( + separated_list!(is_a!(","), address) + + + )); + + named!(pub address_list, ws!(do_parse!( list: alt_complete!( encoded_word_list | ascii_token) >> ( { @@ -278,7 +487,8 @@ named!(pub address_list, ws!(do_parse!( } ) ))); -named!(pub subject, ws!(do_parse!( + +named!(pub phrase, ws!(do_parse!( list: many0!(alt_complete!( encoded_word_list | ascii_token)) >> ( { let string_len = list.iter().fold(0, |mut acc, x| { acc+=x.len(); acc }) + list.len() - 1; @@ -298,52 +508,52 @@ named!(pub subject, ws!(do_parse!( ))); #[test] -fn test_subject() { - let subject_s = "list.free.de mailing list memberships reminder".as_bytes(); +fn test_phrase() { + let phrase_s = "list.free.de mailing list memberships reminder".as_bytes(); assert_eq!( ( &b""[..], "list.free.de mailing list memberships reminder".to_string() ), - subject(subject_s).unwrap() + phrase(phrase_s).unwrap() ); - let subject_s = "=?UTF-8?B?zp3Orc6/IM+Az4HOv8+Dz4nPgM65zrrPjCDOvM6uzr3Phc68zrEgzrHPhs6v?= =?UTF-8?B?z4fOuM63?=".as_bytes(); + let phrase_s = "=?UTF-8?B?zp3Orc6/IM+Az4HOv8+Dz4nPgM65zrrPjCDOvM6uzr3Phc68zrEgzrHPhs6v?= =?UTF-8?B?z4fOuM63?=".as_bytes(); assert_eq!( ( &b""[..], "Νέο προσωπικό μήνυμα αφίχθη".to_string() ), - subject(subject_s).unwrap() + phrase(phrase_s).unwrap() ); - let subject_s = "=?utf-8?B?bW9vZGxlOiDOsc69zrHPg866z4zPgM63z4POtyDOv868zqzOtM6xz4Igz4M=?= =?utf-8?B?z4XOts63z4TOrs+DzrXPic69?=".as_bytes(); + let phrase_s = "=?utf-8?B?bW9vZGxlOiDOsc69zrHPg866z4zPgM63z4POtyDOv868zqzOtM6xz4Igz4M=?= =?utf-8?B?z4XOts63z4TOrs+DzrXPic69?=".as_bytes(); assert_eq!( ( &b""[..], "moodle: ανασκόπηση ομάδας συζητήσεων".to_string() ), - subject(subject_s).unwrap() + phrase(phrase_s).unwrap() ); - let subject_s = + let phrase_s = "=?UTF-8?B?zp3Orc6/IM+Az4HOv8+Dz4nPgM65zrrPjCDOvM6uzr3Phc68zrEgzrHPhs6v?=".as_bytes(); assert_eq!( "Νέο προσωπικό μήνυμα αφί".to_string(), - from_utf8(&encoded_word(subject_s).to_full_result().unwrap()).unwrap() + from_utf8(&encoded_word(phrase_s).to_full_result().unwrap()).unwrap() ); - let subject_s = "=?UTF-8?Q?=CE=A0=CF=81=CF=8C=CF=83=CE=B8=CE=B5?=".as_bytes(); + let phrase_s = "=?UTF-8?Q?=CE=A0=CF=81=CF=8C=CF=83=CE=B8=CE=B5?=".as_bytes(); assert_eq!( "Πρόσθε".to_string(), - from_utf8(&encoded_word(subject_s).to_full_result().unwrap()).unwrap() + from_utf8(&encoded_word(phrase_s).to_full_result().unwrap()).unwrap() ); - let subject_s = "=?iso-8859-7?B?UmU6INDx/OLr5+zhIOzlIPTn7SDh9fHp4e3eIOLh8eTp4Q==?=".as_bytes(); + let phrase_s = "=?iso-8859-7?B?UmU6INDx/OLr5+zhIOzlIPTn7SDh9fHp4e3eIOLh8eTp4Q==?=".as_bytes(); assert_eq!( "Re: Πρόβλημα με την αυριανή βαρδια".to_string(), - from_utf8(&encoded_word(subject_s).to_full_result().unwrap()).unwrap() + from_utf8(&encoded_word(phrase_s).to_full_result().unwrap()).unwrap() ); - let subject_s = "=?UTF-8?Q?=CE=A0=CF=81=CF=8C=CF=83=CE=B8=CE=B5?= + let phrase_s = "=?UTF-8?Q?=CE=A0=CF=81=CF=8C=CF=83=CE=B8=CE=B5?= =?UTF-8?Q?=CF=84=CE=B7_=CE=B5=CE=BE=CE=B5=CF=84?= =?UTF-8?Q?=CE=B1=CF=83=CF=84=CE=B9=CE=BA=CE=AE?=" .as_bytes(); @@ -352,7 +562,7 @@ fn test_subject() { &b""[..], "Πρόσθετη εξεταστική".to_string() ), - subject(subject_s).unwrap() + phrase(phrase_s).unwrap() ); } fn eat_comments(input: &str) -> String { @@ -388,7 +598,7 @@ fn test_eat_comments() { * * We should use a custom parser here*/ pub fn date(input: &str) -> Option> { - let parsed_result = subject(eat_comments(input).as_bytes()) + let parsed_result = phrase(eat_comments(input).as_bytes()) .to_full_result() .unwrap() .replace("-", "+"); diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index d7e7794ee..649737ace 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -1,10 +1,9 @@ use super::*; -use linkify::{LinkFinder, Link}; +use linkify::{Link, LinkFinder}; use std::process::{Command, Stdio}; use mime_apps::query_default_app; - #[derive(PartialEq, Debug)] enum ViewMode { Normal, @@ -35,8 +34,11 @@ pub struct MailView { } impl MailView { - pub fn new(coordinates: (usize, usize, usize), pager: Option, subview: Option>) -> Self { - + pub fn new( + coordinates: (usize, usize, usize), + pager: Option, + subview: Option>, + ) -> Self { MailView { coordinates: coordinates, pager: pager, @@ -49,15 +51,20 @@ impl MailView { } } - impl Component for MailView { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let (envelope_idx, y): (usize, usize) = { - let threaded = context.accounts[self.coordinates.0].runtime_settings.threaded; - let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1].as_ref().unwrap().as_ref().unwrap(); + let threaded = context.accounts[self.coordinates.0] + .runtime_settings + .threaded; + let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); let envelope_idx: usize = if threaded { mailbox.threaded_mail(self.coordinates.2) } else { @@ -66,98 +73,125 @@ impl Component for MailView { let envelope: &Envelope = &mailbox.collection[envelope_idx]; - let (x,y) = write_string_to_grid(&format!("Date: {}", envelope.date_as_str()), - grid, - Color::Byte(33), - Color::Default, - area, - true); + let (x, y) = write_string_to_grid( + &format!("Date: {}", envelope.date_as_str()), + grid, + Color::Byte(33), + Color::Default, + area, + true, + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_fg(Color::Default); } - let (x,y) = write_string_to_grid(&format!("From: {}", envelope.from()), - grid, - Color::Byte(33), - Color::Default, - (set_y(upper_left, y+1), bottom_right), - true); + let (x, y) = write_string_to_grid( + &format!("From: {}", envelope.from_to_string()), + grid, + Color::Byte(33), + Color::Default, + (set_y(upper_left, y + 1), bottom_right), + true, + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_fg(Color::Default); } - let (x,y) = write_string_to_grid(&format!("To: {}", envelope.to()), - grid, - Color::Byte(33), - Color::Default, - (set_y(upper_left, y+1), bottom_right), - true); + let (x, y) = write_string_to_grid( + &format!("To: {}", envelope.to_to_string()), + grid, + Color::Byte(33), + Color::Default, + (set_y(upper_left, y + 1), bottom_right), + true, + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_fg(Color::Default); } - let (x,y) = write_string_to_grid(&format!("Subject: {}", envelope.subject()), - grid, - Color::Byte(33), - Color::Default, - (set_y(upper_left, y+1), bottom_right), - true); + let (x, y) = write_string_to_grid( + &format!("Subject: {}", envelope.subject()), + grid, + Color::Byte(33), + Color::Default, + (set_y(upper_left, y + 1), bottom_right), + true, + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_fg(Color::Default); } - let (x, y) = write_string_to_grid(&format!("Message-ID: {}", envelope.message_id_raw()), - grid, - Color::Byte(33), - Color::Default, - (set_y(upper_left, y+1), bottom_right), - true); + let (x, y) = write_string_to_grid( + &format!("Message-ID: {}", envelope.message_id_raw()), + grid, + Color::Byte(33), + Color::Default, + (set_y(upper_left, y + 1), bottom_right), + true, + ); for x in x..=get_x(bottom_right) { grid[(x, y)].set_ch(' '); grid[(x, y)].set_bg(Color::Default); grid[(x, y)].set_fg(Color::Default); } - clear_area(grid, - (set_y(upper_left, y+1), set_y(bottom_right, y+2))); - context.dirty_areas.push_back((upper_left, set_y(bottom_right, y+1))); + clear_area(grid, (set_y(upper_left, y + 1), set_y(bottom_right, y + 2))); + context + .dirty_areas + .push_back((upper_left, set_y(bottom_right, y + 1))); (envelope_idx, y + 1) }; if self.dirty { let buf = { - let mailbox_idx = self.coordinates; // coordinates are mailbox idxs - let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1].as_ref().unwrap().as_ref().unwrap(); - let envelope : &Envelope = &mailbox.collection[envelope_idx]; + let mailbox_idx = self.coordinates; // coordinates are mailbox idxs + let mailbox = &mut context.accounts[mailbox_idx.0][mailbox_idx.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); + let envelope: &Envelope = &mailbox.collection[envelope_idx]; let finder = LinkFinder::new(); let mut text = match self.mode { ViewMode::Url => { let mut t = envelope.body().text().to_string(); for (lidx, l) in finder.links(&envelope.body().text()).enumerate() { - t.insert_str(l.start()+(lidx*3), &format!("[{}]", lidx)); + t.insert_str(l.start() + (lidx * 3), &format!("[{}]", lidx)); } if envelope.body().count_attachments() > 1 { - t = envelope.body().attachments().iter().enumerate().fold(t, |mut s, (idx, a)| { s.push_str(&format!("[{}] {}\n\n", idx, a)); s }); + t = envelope.body().attachments().iter().enumerate().fold( + t, + |mut s, (idx, a)| { + s.push_str(&format!("[{}] {}\n\n", idx, a)); + s + }, + ); } t - }, + } ViewMode::Attachment(aidx) => { let attachments = envelope.body().attachments(); let mut ret = format!("Viewing attachment. Press `r` to return \n"); ret.push_str(&attachments[aidx].text()); ret - }, + } _ => { let mut t = envelope.body().text().to_string(); if envelope.body().count_attachments() > 1 { - t = envelope.body().attachments().iter().enumerate().fold(t, |mut s, (idx, a)| { s.push_str(&format!("[{}] {}\n\n", idx, a)); s }); + t = envelope.body().attachments().iter().enumerate().fold( + t, + |mut s, (idx, a)| { + s.push_str(&format!("[{}] {}\n\n", idx, a)); + s + }, + ); } t - }, + } }; let mut buf = CellBuffer::from(&text); match self.mode { @@ -172,10 +206,10 @@ impl Component for MailView { buf[(l.start() + shift - 3, 0)].set_fg(Color::Byte(226)); } // Each Cell represents one char so next line will be: - shift += r.chars().count()+1; + shift += r.chars().count() + 1; } - }, - _ => {}, + } + _ => {} } buf }; @@ -188,29 +222,41 @@ impl Component for MailView { self.pager = Some(Pager::from_buf(buf, cursor_pos)); self.dirty = false; } - self.pager.as_mut().map(|p| p.draw(grid, (set_y(upper_left, y + 1),bottom_right), context)); + self.pager + .as_mut() + .map(|p| p.draw(grid, (set_y(upper_left, y + 1), bottom_right), context)); } - fn process_event(&mut self, event: &UIEvent, context: &mut Context) { match event.event_type { UIEventType::Input(Key::Esc) => { self.cmd_buf.clear(); - }, - UIEventType::Input(Key::Char(c)) if c >= '0' && c <= '9' => { //TODO:this should be an Action + } + UIEventType::Input(Key::Char(c)) if c >= '0' && c <= '9' => { + //TODO:this should be an Action self.cmd_buf.push(c); - }, - UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() => { //TODO:one quit shortcut? + } + UIEventType::Input(Key::Char('r')) if self.mode.is_attachment() => { + //TODO:one quit shortcut? self.mode = ViewMode::Normal; self.dirty = true; - }, - UIEventType::Input(Key::Char('a')) if self.cmd_buf.len() > 0 && self.mode == ViewMode::Normal => { //TODO:this should be an Action + } + UIEventType::Input(Key::Char('a')) + if self.cmd_buf.len() > 0 && self.mode == ViewMode::Normal => + { + //TODO:this should be an Action let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); { - let threaded = context.accounts[self.coordinates.0].runtime_settings.threaded; - let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1].as_ref().unwrap().as_ref().unwrap(); + let threaded = context.accounts[self.coordinates.0] + .runtime_settings + .threaded; + let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); let envelope_idx: usize = if threaded { mailbox.threaded_mail(self.coordinates.2) } else { @@ -223,11 +269,16 @@ impl Component for MailView { ContentType::Text => { self.mode = ViewMode::Attachment(lidx); self.dirty = true; - }, + } ContentType::Multipart { .. } => { - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::StatusNotification(format!("Multipart attachments are not supported yet.")) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::StatusNotification(format!( + "Multipart attachments are not supported yet." + )), + }); return; - }, + } ContentType::Unsupported { .. } => { let attachment_type = u.mime_type(); let binary = query_default_app(&attachment_type); @@ -240,28 +291,45 @@ impl Component for MailView { .spawn() .expect(&format!("Failed to start {}", binary.display())); } else { - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::StatusNotification(format!("Couldn't find a default application for type {}", attachment_type)) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::StatusNotification(format!( + "Couldn't find a default application for type {}", + attachment_type + )), + }); return; } - - }, + } } - } else { - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::StatusNotification(format!("Attachment `{}` not found.", lidx)) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::StatusNotification(format!( + "Attachment `{}` not found.", + lidx + )), + }); return; } }; - - - }, - UIEventType::Input(Key::Char('g')) if self.cmd_buf.len() > 0 && self.mode == ViewMode::Url => { //TODO:this should be an Action + } + UIEventType::Input(Key::Char('g')) + if self.cmd_buf.len() > 0 && self.mode == ViewMode::Url => + { + //TODO:this should be an Action let lidx = self.cmd_buf.parse::().unwrap(); self.cmd_buf.clear(); let url = { - let threaded = context.accounts[self.coordinates.0].runtime_settings.threaded; - let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1].as_ref().unwrap().as_ref().unwrap(); + let threaded = context.accounts[self.coordinates.0] + .runtime_settings + .threaded; + let mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] + .as_ref() + .unwrap() + .as_ref() + .unwrap(); let envelope_idx: usize = if threaded { mailbox.threaded_mail(self.coordinates.2) } else { @@ -275,33 +343,37 @@ impl Component for MailView { if let Some(u) = links.get(lidx) { u.as_str().to_string() } else { - context.replies.push_back(UIEvent { id: 0, event_type: UIEventType::StatusNotification(format!("Link `{}` not found.", lidx)) }); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::StatusNotification(format!( + "Link `{}` not found.", + lidx + )), + }); return; } }; - Command::new("xdg-open") .arg(url) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() .expect("Failed to start xdg_open"); - - }, - UIEventType::Input(Key::Char('u')) => { //TODO:this should be an Action + } + UIEventType::Input(Key::Char('u')) => { + //TODO:this should be an Action match self.mode { - ViewMode::Normal => { self.mode = ViewMode::Url }, - ViewMode::Url => { self.mode = ViewMode::Normal }, - _ => {}, + ViewMode::Normal => self.mode = ViewMode::Url, + ViewMode::Url => self.mode = ViewMode::Normal, + _ => {} } self.dirty = true; - }, - _ => {}, + } + _ => {} } if let Some(ref mut sub) = self.subview { sub.process_event(event, context); - } else { if let Some(ref mut p) = self.pager { p.process_event(event, context); @@ -309,7 +381,8 @@ impl Component for MailView { } } fn is_dirty(&self) -> bool { - self.dirty || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false) || - self.subview.as_ref().map(|p| p.is_dirty()).unwrap_or(false) + self.dirty + || self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false) + || self.subview.as_ref().map(|p| p.is_dirty()).unwrap_or(false) } }