diff --git a/benches/parse.rs b/benches/parse.rs index c1fced803..12a93961d 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -10,6 +10,9 @@ use self::test::Bencher; #[bench] fn mail_parse(b: &mut Bencher) { - b.iter(|| Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || { - Box::new(MaildirOp::new("test/attachment_test".to_string()))})))) ); + b.iter(|| { + Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || { + Box::new(MaildirOp::new("test/attachment_test".to_string())) + })))) + }); } diff --git a/src/bin.rs b/src/bin.rs index bc21d1f1d..d4f78b367 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -37,17 +37,13 @@ fn main() { let mut j = 0; let folder_length = set.accounts["norn"].folders.len(); let mut account = Account::new("norn".to_string(), set.accounts["norn"].clone()); - 'main : loop { + 'main: loop { ncurses::touchwin(ncurses::stdscr()); - ncurses::mv(0,0); + ncurses::mv(0, 0); let mailbox = &mut account[j]; let mut index: Box = match *mailbox.as_ref().unwrap() { - Ok(ref v) => { - Box::new(Index::new(v)) - }, - Err(ref v) => { - Box::new(ErrorWindow::new((*v).clone())) - } + Ok(ref v) => Box::new(Index::new(v)), + Err(ref v) => Box::new(ErrorWindow::new((*v).clone())), }; //eprintln!("{:?}", set); ncurses::refresh(); @@ -55,39 +51,35 @@ fn main() { index.draw(); let mut ch; - 'inner : loop { + 'inner: loop { ch = ncurses::get_wch(); match ch { Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_UP)) | - Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => { - index.handle_input(k); - continue; - } + Some(ncurses::WchResult::KeyCode(k @ ncurses::KEY_DOWN)) => { + index.handle_input(k); + continue; + } Some(ncurses::WchResult::Char(k @ 10)) => { index.handle_input(k as i32); continue; } Some(ncurses::WchResult::KeyCode(ncurses::KEY_F1)) | - Some(ncurses::WchResult::Char(113)) => { - break 'main; - }, - Some(ncurses::WchResult::Char(74)) => { - if j < folder_length - 1 { - j += 1; - break 'inner; - } + Some(ncurses::WchResult::Char(113)) => { + break 'main; + } + Some(ncurses::WchResult::Char(74)) => if j < folder_length - 1 { + j += 1; + break 'inner; }, - Some(ncurses::WchResult::Char(75)) => { - if j > 0 { - j -= 1; - break 'inner; - } + Some(ncurses::WchResult::Char(75)) => if j > 0 { + j -= 1; + break 'inner; }, Some(ncurses::WchResult::KeyCode(ncurses::KEY_RESIZE)) => { eprintln!("key_resize"); index.redraw(); continue; - }, + } _ => {} } } diff --git a/src/conf/mod.rs b/src/conf/mod.rs index 826a6c290..f6efe97a0 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -19,27 +19,23 @@ * along with meli. If not, see . */ -extern crate xdg; extern crate config; +extern crate xdg; use std::collections::HashMap; use std::io; use std::fs; -use std::path::{PathBuf, Path}; +use std::path::{Path, PathBuf}; -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub enum MailFormat { - Maildir + Maildir, } impl MailFormat { pub fn from_str(x: &str) -> MailFormat { match x.to_lowercase().as_ref() { - "maildir" => { - MailFormat::Maildir - }, - _ => { - panic!("Unrecognizable mail format") - } + "maildir" => MailFormat::Maildir, + _ => panic!("Unrecognizable mail format"), } } } @@ -49,7 +45,7 @@ struct FileAccount { folders: String, format: String, sent_folder: String, - threaded : bool, + threaded: bool, } #[derive(Debug, Deserialize, Default)] @@ -57,16 +53,16 @@ struct FileSettings { accounts: HashMap, } -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub struct AccountSettings { pub folders: Vec, format: MailFormat, pub sent_folder: String, - threaded : bool, + threaded: bool, } #[derive(Debug)] pub struct Settings { - pub accounts: HashMap, + pub accounts: HashMap, } @@ -74,8 +70,9 @@ use self::config::{Config, File, FileFormat}; impl FileSettings { pub fn new() -> FileSettings { let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap(); - let config_path = xdg_dirs.place_config_file("config") - .expect("cannot create configuration directory"); + let config_path = xdg_dirs + .place_config_file("config") + .expect("cannot create configuration directory"); //let setts = Config::default().merge(File::new(config_path.to_str().unwrap_or_default(), config::FileFormat::Toml)).unwrap(); let mut s = Config::new(); let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml)); @@ -83,10 +80,14 @@ impl FileSettings { if s.is_ok() { s.unwrap().deserialize().unwrap() } else { - eprintln!("{:?}",s.err().unwrap()); + eprintln!("{:?}", s.err().unwrap()); let mut buf = String::new(); - io::stdin().read_line(&mut buf).expect("Failed to read line"); - FileSettings { ..Default::default() } + io::stdin() + .read_line(&mut buf) + .expect("Failed to read line"); + FileSettings { + ..Default::default() + } } } } @@ -99,40 +100,39 @@ impl Settings { for (id, x) in fs.accounts { let mut folders = Vec::new(); fn recurse_folders>(folders: &mut Vec, p: P) { - for mut f in fs::read_dir(p).unwrap() { - for f in f.iter_mut() { - { - let path = f.path(); - if path.ends_with("cur") || path.ends_with("new") || - path.ends_with("tmp") { + for mut f in fs::read_dir(p).unwrap() { + for f in f.iter_mut() { + { + let path = f.path(); + if path.ends_with("cur") || path.ends_with("new") || + path.ends_with("tmp") + { continue; - } - if path.is_dir() { - folders.push(path.to_str().unwrap().to_string()); - recurse_folders(folders, path); + } + if path.is_dir() { + folders.push(path.to_str().unwrap().to_string()); + recurse_folders(folders, path); + } } } } - - } }; let path = PathBuf::from(&x.folders); if path.is_dir() { folders.push(path.to_str().unwrap().to_string()); } recurse_folders(&mut folders, &x.folders); - s.insert(id.clone(), AccountSettings { - folders: folders, - format: MailFormat::from_str(&x.format), - sent_folder: x.sent_folder.clone(), - threaded: x.threaded, - }); - - + s.insert( + id.clone(), + AccountSettings { + folders: folders, + format: MailFormat::from_str(&x.format), + sent_folder: x.sent_folder.clone(), + threaded: x.threaded, + }, + ); } Settings { accounts: s } - - } } diff --git a/src/error.rs b/src/error.rs index d8d028bbd..ee272fbee 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,20 +27,25 @@ use nom; pub type Result = result::Result; -#[derive(Debug,Clone)] +#[derive(Debug, Clone)] pub struct MeliError { - details: String + details: String, } impl MeliError { - pub fn new(msg: M) -> MeliError where M: Into { - MeliError{details: msg.into()} + pub fn new(msg: M) -> MeliError + where + M: Into, + { + MeliError { + details: msg.into(), + } } } impl fmt::Display for MeliError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f,"{}",self.details) + write!(f, "{}", self.details) } } diff --git a/src/mailbox/accounts.rs b/src/mailbox/accounts.rs index a040ad23b..828cabf92 100644 --- a/src/mailbox/accounts.rs +++ b/src/mailbox/accounts.rs @@ -21,7 +21,7 @@ use mailbox::*; use conf::AccountSettings; -use std::ops::{IndexMut, Index}; +use std::ops::{Index, IndexMut}; #[derive(Debug)] pub struct Account { name: String, @@ -29,19 +29,21 @@ pub struct Account { sent_folder: Option, - settings: AccountSettings, } impl Account { pub fn new(name: String, settings: AccountSettings) -> Self { - eprintln!("new acc" ); - let sent_folder = settings.folders.iter().position(|x| *x == settings.sent_folder); - let mut folders = Vec::with_capacity(settings.folders.len()); - for _ in 0..settings.folders.len() { - folders.push(None); - } + eprintln!("new acc"); + let sent_folder = settings + .folders + .iter() + .position(|x| *x == settings.sent_folder); + let mut folders = Vec::with_capacity(settings.folders.len()); + for _ in 0..settings.folders.len() { + folders.push(None); + } Account { name: name, folders: folders, @@ -60,8 +62,7 @@ impl Index for Account { } } -impl IndexMut for Account -{ +impl IndexMut for Account { fn index_mut(&mut self, index: usize) -> &mut Option> { if self.folders[index].is_none() { eprintln!("building folder {:?}", self.settings.folders[index]); @@ -73,14 +74,18 @@ impl IndexMut for Account self.folders[index] = Some(Mailbox::new(&path, &None)); eprintln!("Done!"); } else { - eprintln!("Now building folder {:?} with sent_folder", self.settings.folders[index]); - let (sent, cur) = - { + eprintln!( + "Now building folder {:?} with sent_folder", + self.settings.folders[index] + ); + let (sent, cur) = { let ptr = self.folders.as_mut_ptr(); unsafe { use std::slice::from_raw_parts_mut; - (from_raw_parts_mut(ptr.offset(id as isize), id+1), - from_raw_parts_mut(ptr.offset(index as isize), index+1)) + ( + from_raw_parts_mut(ptr.offset(id as isize), id + 1), + from_raw_parts_mut(ptr.offset(index as isize), index + 1), + ) } }; let sent_path = self.settings.folders[id].clone(); @@ -93,13 +98,14 @@ impl IndexMut for Account eprintln!("Done!"); } } else { - eprintln!("Now building folder {:?} without sent_folder", self.settings.folders[index]); - self.folders[index] = Some(Mailbox::new(&path, &None)); - eprintln!("Done!"); + eprintln!( + "Now building folder {:?} without sent_folder", + self.settings.folders[index] + ); + self.folders[index] = Some(Mailbox::new(&path, &None)); + eprintln!("Done!"); }; } &mut self.folders[index] } - - } diff --git a/src/mailbox/backends/maildir.rs b/src/mailbox/backends/maildir.rs index 1fc9a01b3..aa417ee27 100644 --- a/src/mailbox/backends/maildir.rs +++ b/src/mailbox/backends/maildir.rs @@ -21,7 +21,7 @@ use mailbox::email::Envelope; use error::{MeliError, Result}; -use mailbox::backends::{MailBackend, BackendOp, BackendOpGenerator}; +use mailbox::backends::{BackendOp, BackendOpGenerator, MailBackend}; use mailbox::email::parser; extern crate crossbeam; @@ -33,7 +33,7 @@ pub struct MaildirType { path: String, } -#[derive(Debug,Default)] +#[derive(Debug, Default)] pub struct MaildirOp { path: String, slice: Option, @@ -59,11 +59,13 @@ impl MaildirOp { impl BackendOp for MaildirOp { fn description(&self) -> String { - format!("Path of file: {}", self.path) + format!("Path of file: {}", self.path) } fn as_bytes(&mut self) -> Result<&[u8]> { if self.slice.is_none() { - self.slice = Some(Mmap::open_path(self.path.to_string(), Protection::Read).unwrap()); + self.slice = Some( + Mmap::open_path(self.path.to_string(), Protection::Read).unwrap(), + ); } Ok(unsafe { self.slice.as_ref().unwrap().as_slice() }) } @@ -110,7 +112,7 @@ impl MailBackend for MaildirType { impl MaildirType { pub fn new(path: &str) -> Self { MaildirType { - path: path.to_string() + path: path.to_string(), } } fn is_valid(path: &str) -> Result<()> { @@ -118,7 +120,9 @@ impl MaildirType { for d in &["cur", "new", "tmp"] { p.push(d); if !p.is_dir() { - return Err(MeliError::new(format!("{} is not a valid maildir folder", path))); + return Err(MeliError::new( + format!("{} is not a valid maildir folder", path), + )); } p.pop(); } @@ -155,7 +159,6 @@ panic!("didn't parse"); }, r.push(m); */ - } let mut threads = Vec::with_capacity(cores); if !files.is_empty() { @@ -163,16 +166,16 @@ panic!("didn't parse"); }, let chunk_size = if count / cores > 0 { count / cores } else { - count + count }; for chunk in files.chunks(chunk_size) { let s = scope.spawn(move || { - let mut local_r:Vec = Vec::with_capacity(chunk.len()); + let mut local_r: Vec = Vec::with_capacity(chunk.len()); for e in chunk { let e_copy = e.to_string(); - if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new(Box::new(move || { - Box::new(MaildirOp::new(e_copy.clone())) - } )))) { + if let Some(e) = Envelope::from(Box::new(BackendOpGenerator::new( + Box::new(move || Box::new(MaildirOp::new(e_copy.clone()))), + ))) { local_r.push(e); } } diff --git a/src/mailbox/backends/mod.rs b/src/mailbox/backends/mod.rs index 33696ee58..8bf987d51 100644 --- a/src/mailbox/backends/mod.rs +++ b/src/mailbox/backends/mod.rs @@ -75,9 +75,9 @@ pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send { fn fetch_body(&mut self) -> Result<&[u8]>; } -/// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object +/// `BackendOpGenerator` is a wrapper for a closure that returns a `BackendOp` object /// See `BackendOp` for details. - /* +/* * I know this sucks, but that's the best way I found that rustc deems safe. * */ pub struct BackendOpGenerator(Box Box>); @@ -97,4 +97,3 @@ impl fmt::Debug for BackendOpGenerator { write!(f, "BackendOpGenerator: {}", op.description()) } } - diff --git a/src/mailbox/email/attachments.rs b/src/mailbox/email/attachments.rs index 4efcaa0e8..c44cc623b 100644 --- a/src/mailbox/email/attachments.rs +++ b/src/mailbox/email/attachments.rs @@ -30,20 +30,23 @@ use std::ascii::AsciiExt; * Multipart */ -#[derive(Clone,Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum MultipartType { Mixed, Alternative, Digest, Unsupported { tag: String }, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub enum AttachmentType { Data { tag: String }, Text { content: String }, - Multipart { of_type: MultipartType, subattachments: Vec, } + Multipart { + of_type: MultipartType, + subattachments: Vec, + }, } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub enum ContentType { Text, Multipart { boundary: String }, @@ -53,19 +56,13 @@ pub enum ContentType { impl Display for ContentType { fn fmt(&self, f: &mut Formatter) -> Result { match *self { - ContentType::Text => { - write!(f, "text") - }, - ContentType::Multipart { .. } => { - write!(f, "multipart") - }, - ContentType::Unsupported { tag: ref t } => { - write!(f, "{}", t) - }, + ContentType::Text => write!(f, "text"), + ContentType::Multipart { .. } => write!(f, "multipart"), + ContentType::Unsupported { tag: ref t } => write!(f, "{}", t), } } } -#[derive(Clone,Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum ContentSubType { Plain, Other { tag: String }, @@ -73,16 +70,12 @@ pub enum ContentSubType { impl Display for ContentSubType { fn fmt(&self, f: &mut Formatter) -> Result { match *self { - ContentSubType::Plain => { - write!(f, "plain") - }, - ContentSubType::Other { tag: ref t } => { - write!(f, "{}", t) - }, + ContentSubType::Plain => write!(f, "plain"), + ContentSubType::Other { tag: ref t } => write!(f, "{}", t), } } } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub enum ContentTransferEncoding { _8Bit, _7Bit, @@ -110,111 +103,111 @@ impl AttachmentBuilder { } 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") { - let mut boundary = None; - for (n, v) in params { - if n.eq_ignore_ascii_case("boundary") { - boundary = Some(format!("--{}--", v).to_string()); - break; - } + Ok((ct, cst, params)) => if ct.eq_ignore_ascii_case("multipart") { + let mut boundary = None; + for (n, v) in params { + if n.eq_ignore_ascii_case("boundary") { + boundary = Some(format!("--{}--", v).to_string()); + break; } - assert!(boundary.is_some()); - self.content_type.0 = ContentType::Multipart { boundary: boundary.unwrap() }; - self.content_type.1 = ContentSubType::Other { tag: cst.to_string() }; - } else if ct.eq_ignore_ascii_case("text") { - self.content_type.0 = ContentType::Text; - if !cst.eq_ignore_ascii_case("plain") { - self.content_type.1 = ContentSubType::Other { tag: cst.to_ascii_lowercase() }; - } - } else { - self.content_type.0 = ContentType::Unsupported { tag: ct.to_ascii_lowercase() }; - self.content_type.1 = ContentSubType::Other { tag: cst.to_ascii_lowercase() }; } + assert!(boundary.is_some()); + self.content_type.0 = ContentType::Multipart { + boundary: boundary.unwrap(), + }; + self.content_type.1 = ContentSubType::Other { + tag: cst.to_string(), + }; + } else if ct.eq_ignore_ascii_case("text") { + self.content_type.0 = ContentType::Text; + if !cst.eq_ignore_ascii_case("plain") { + self.content_type.1 = ContentSubType::Other { + tag: cst.to_ascii_lowercase(), + }; + } + } else { + self.content_type.0 = ContentType::Unsupported { + tag: ct.to_ascii_lowercase(), + }; + self.content_type.1 = ContentSubType::Other { + tag: cst.to_ascii_lowercase(), + }; }, - Err(v) => { - eprintln!("parsing error in content_type: {:?} {:?}", value, v); - } -} -self + Err(v) => { + eprintln!("parsing error in content_type: {:?} {:?}", value, v); + } + } + self } pub fn content_transfer_encoding(&mut self, value: &str) -> &Self { - self.content_transfer_encoding = - if value.eq_ignore_ascii_case("base64") { - ContentTransferEncoding::Base64 - } else if value.eq_ignore_ascii_case("7bit") { - ContentTransferEncoding::_7Bit - } else if value.eq_ignore_ascii_case("8bit") { - ContentTransferEncoding::_8Bit - } else if value.eq_ignore_ascii_case("quoted-printable") { - ContentTransferEncoding::QuotedPrintable - } else { - ContentTransferEncoding::Other { tag: value.to_ascii_lowercase() } - }; + self.content_transfer_encoding = if value.eq_ignore_ascii_case("base64") { + ContentTransferEncoding::Base64 + } else if value.eq_ignore_ascii_case("7bit") { + ContentTransferEncoding::_7Bit + } else if value.eq_ignore_ascii_case("8bit") { + ContentTransferEncoding::_8Bit + } else if value.eq_ignore_ascii_case("quoted-printable") { + ContentTransferEncoding::QuotedPrintable + } else { + ContentTransferEncoding::Other { + tag: value.to_ascii_lowercase(), + } + }; self } fn decode(&self) -> String { - match self.content_transfer_encoding { - ContentTransferEncoding::Base64 => { - match ::base64::decode(&::std::str::from_utf8(&self.raw).unwrap().trim().lines().fold(String::with_capacity(self.raw.len()), |mut acc, x| { acc.push_str(x); acc })) { - 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() - } - }, - _ => { - String::from_utf8_lossy(&self.raw).into_owned() + match self.content_transfer_encoding { + ContentTransferEncoding::Base64 => { + match ::base64::decode(&::std::str::from_utf8(&self.raw) + .unwrap() + .trim() + .lines() + .fold(String::with_capacity(self.raw.len()), |mut acc, x| { + acc.push_str(x); + acc + })) { + 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() } } - }, - ContentTransferEncoding::QuotedPrintable => { - parser::quoted_printable_text(&self.raw).to_full_result().unwrap() - }, - ContentTransferEncoding::_7Bit | ContentTransferEncoding::_8Bit | ContentTransferEncoding::Other { .. } => { - String::from_utf8_lossy(&self.raw).into_owned() + _ => String::from_utf8_lossy(&self.raw).into_owned(), } } + ContentTransferEncoding::QuotedPrintable => parser::quoted_printable_text(&self.raw) + .to_full_result() + .unwrap(), + ContentTransferEncoding::_7Bit | + ContentTransferEncoding::_8Bit | + ContentTransferEncoding::Other { .. } => { + String::from_utf8_lossy(&self.raw).into_owned() + } + } } pub fn build(self) -> Attachment { - let attachment_type = - match self.content_type.0 { - ContentType::Text => { - AttachmentType::Text { content: self.decode() } + let attachment_type = match self.content_type.0 { + ContentType::Text => AttachmentType::Text { + content: self.decode(), }, 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() } - }, - } - }, - _ => { - panic!() - } - }; + 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() }, + }, + _ => panic!(), + }; AttachmentType::Multipart { of_type: multipart_type, subattachments: Attachment::subattachments(&self.raw, b), } - }, - ContentType::Unsupported { ref tag } => { - AttachmentType::Data { tag: tag.clone() } - }, + } + ContentType::Unsupported { ref tag } => AttachmentType::Data { tag: tag.clone() }, }; Attachment { content_type: self.content_type, @@ -222,13 +215,11 @@ self raw: self.raw, attachment_type: attachment_type, } - - } } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] pub struct Attachment { content_type: (ContentType, ContentSubType), content_transfer_encoding: ContentTransferEncoding, @@ -243,27 +234,25 @@ impl Attachment { match self.attachment_type { AttachmentType::Data { .. } => { text.push_str(&format!("Data attachment of type {}", self.get_tag())); - }, + } AttachmentType::Text { content: ref t } => { text.push_str(t); - }, + } AttachmentType::Multipart { of_type: ref multipart_type, subattachments: ref sub_att_vec, - } => { - if *multipart_type == MultipartType::Alternative { - for a in sub_att_vec { - if a.content_type.1 == ContentSubType::Plain { - a.get_text_recursive(text); - break; - } - } - } else { - for a in sub_att_vec { + } => if *multipart_type == MultipartType::Alternative { + for a in sub_att_vec { + if a.content_type.1 == ContentSubType::Plain { a.get_text_recursive(text); - text.push_str("\n\n"); + break; } } + } else { + for a in sub_att_vec { + a.get_text_recursive(text); + text.push_str("\n\n"); + } }, } } @@ -280,7 +269,8 @@ impl Attachment { } pub fn subattachments(raw: &[u8], boundary: &str) -> Vec { let boundary_length = boundary.len(); - match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result() { + match parser::attachments(raw, &boundary[0..boundary_length - 2], boundary).to_full_result() + { Ok(attachments) => { let mut vec = Vec::with_capacity(attachments.len()); for a in attachments { @@ -292,26 +282,30 @@ impl Attachment { eprintln!("{}\n", ::std::string::String::from_utf8_lossy(a)); eprintln!("-------------------------------\n"); - continue + continue; } }; let mut builder = AttachmentBuilder::new(body); for (name, value) in headers { if name.eq_ignore_ascii_case("content-type") { builder.content_type(value); - } else if name.eq_ignore_ascii_case("content-transfer-encoding") { + } else if name.eq_ignore_ascii_case("content-transfer-encoding") { builder.content_transfer_encoding(value); } } vec.push(builder.build()); } vec - }, + } a => { - eprintln!("error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}", a, ::std::str::from_utf8(raw).unwrap(), boundary); + eprintln!( + "error in 469 {:?}\n\traw: {:?}\n\tboundary: {:?}", + a, + ::std::str::from_utf8(raw).unwrap(), + boundary + ); Vec::new() - }, - + } } } } diff --git a/src/mailbox/email/mod.rs b/src/mailbox/email/mod.rs index d4c420d97..075fdbd58 100644 --- a/src/mailbox/email/mod.rs +++ b/src/mailbox/email/mod.rs @@ -25,6 +25,7 @@ use mailbox::backends::BackendOpGenerator; use self::attachments::*; use std::string::String; +use std::sync::Arc; //use std; use std::cmp::Ordering; use std::fmt; @@ -36,7 +37,7 @@ use chrono; use chrono::TimeZone; /* Helper struct to return slices from a struct on demand */ -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] struct StrBuilder { offset: usize, length: usize, @@ -49,15 +50,18 @@ pub trait StrBuild { } #[derive(Clone)] -pub struct MessageID (String, StrBuilder); +pub struct MessageID(String, StrBuilder); impl StrBuild for MessageID { fn new(string: &str, slice: &str) -> Self { let offset = string.find(slice).unwrap(); - MessageID (string.to_string(), StrBuilder { - offset: offset, - length: slice.len() + 1, - }) + MessageID( + string.to_string(), + StrBuilder { + offset: offset, + length: slice.len() + 1, + }, + ) } fn get_raw(&self) -> &str { let offset = self.1.offset; @@ -73,7 +77,16 @@ impl StrBuild for MessageID { fn test_strbuilder() { let m_id = "<20170825132332.6734-1-el13635@mail.ntua.gr>"; let (_, slice) = parser::message_id(m_id.as_bytes()).unwrap(); - assert_eq!(MessageID::new(m_id, slice), MessageID (m_id.to_string(), StrBuilder{offset: 1, length: 43})); + assert_eq!( + MessageID::new(m_id, slice), + MessageID( + m_id.to_string(), + StrBuilder { + offset: 1, + length: 43, + } + ) + ); } impl PartialEq for MessageID { @@ -87,17 +100,15 @@ impl fmt::Debug for MessageID { } } -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] struct References { raw: String, refs: Vec, } -use std::sync::Arc; /* A very primitive mail object */ #[derive(Debug, Clone)] -pub struct Envelope -{ +pub struct Envelope { date: String, from: Option, to: Option, @@ -115,13 +126,16 @@ pub struct Envelope } -impl Envelope -{ +impl Envelope { pub fn get_date(&self) -> i64 { - self.timestamp + self.timestamp } pub fn get_datetime(&self) -> chrono::DateTime { - self.datetime.unwrap_or_else(|| { chrono::FixedOffset::west(0).ymd(1970, 1, 1).and_hms(0, 0, 0)}) + self.datetime.unwrap_or_else(|| { + chrono::FixedOffset::west(0) + .ymd(1970, 1, 1) + .and_hms(0, 0, 0) + }) } pub fn get_date_as_str(&self) -> &str { &self.date @@ -147,7 +161,7 @@ impl Envelope let operation = self.operation_token.generate(); eprintln!("error in parsing mail\n{}", operation.description()); panic!() - }, + } }; let mut builder = AttachmentBuilder::new(body); for (name, value) in headers { @@ -165,19 +179,19 @@ impl Envelope pub fn get_subject(&self) -> &str { match self.subject { Some(ref s) => s, - _ => "" + _ => "", } } pub fn get_in_reply_to(&self) -> &str { match self.in_reply_to { Some(ref s) => s.get_val(), - _ => "" + _ => "", } } pub fn get_in_reply_to_raw(&self) -> &str { match self.in_reply_to { Some(ref s) => s.get_raw(), - _ => "" + _ => "", } } pub fn get_message_id(&self) -> &str { @@ -203,10 +217,12 @@ impl Envelope } fn set_in_reply_to(&mut self, new_val: &str) -> () { let slice = match parser::message_id(new_val.as_bytes()).to_full_result() { - Ok(v) => { v }, - Err(v) => { eprintln!("{} {:?}",new_val, v); + Ok(v) => v, + Err(v) => { + eprintln!("{} {:?}", new_val, v); self.in_reply_to = None; - return; } + return; + } }; self.in_reply_to = Some(MessageID::new(new_val, slice)); } @@ -215,23 +231,27 @@ impl Envelope } fn set_message_id(&mut self, new_val: &str) -> () { let slice = match parser::message_id(new_val.as_bytes()).to_full_result() { - Ok(v) => { v }, - Err(v) => { eprintln!("{} {:?}",new_val, v); + Ok(v) => v, + Err(v) => { + eprintln!("{} {:?}", new_val, v); self.message_id = None; - return; } + return; + } }; 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() { - Ok(v) => { v }, - Err(v) => { eprintln!("{} {:?}",new_val, v); - return; } + Ok(v) => v, + Err(v) => { + eprintln!("{} {:?}", new_val, v); + return; + } }; let new_ref = MessageID::new(new_val, slice); match self.references { Some(ref mut s) => { - if s.refs.contains(&new_ref) { + if s.refs.contains(&new_ref) { if s.refs[s.refs.len() - 1] != new_ref { if let Some(index) = s.refs.iter().position(|x| *x == new_ref) { s.refs.remove(index); @@ -243,29 +263,39 @@ impl Envelope } } s.refs.push(new_ref); - }, + } None => { let mut v = Vec::new(); v.push(new_ref); - self.references = Some(References { raw: "".to_string(), refs: v, }); + self.references = Some(References { + raw: "".to_string(), + refs: v, + }); } } - } fn set_references(&mut self, new_val: String) -> () { match self.references { Some(ref mut s) => { s.raw = new_val; - }, + } None => { let v = Vec::new(); - self.references = Some(References { raw: new_val, refs: v, }); + self.references = Some(References { + raw: new_val, + refs: v, + }); } } } pub fn get_references(&self) -> Vec<&MessageID> { match self.references { - Some(ref s) => s.refs.iter().fold(Vec::with_capacity(s.refs.len()), |mut acc, x| { acc.push(x); acc }), + Some(ref s) => s.refs + .iter() + .fold(Vec::with_capacity(s.refs.len()), |mut acc, x| { + acc.push(x); + acc + }), None => Vec::new(), } } @@ -276,7 +306,7 @@ impl Envelope self.thread } pub fn set_thread(&mut self, new_val: usize) -> () { - self.thread = new_val; + self.thread = new_val; } pub fn set_datetime(&mut self, new_val: Option>) -> () { self.datetime = new_val; @@ -301,20 +331,17 @@ impl Envelope thread: 0, operation_token: Arc::new(token), - } } pub fn from(operation_token: Box) -> Option { let mut operation = operation_token.generate(); let headers = match parser::headers(operation.fetch_headers().unwrap()).to_full_result() { - Ok(v) => { - v - }, + Ok(v) => v, _ => { let operation = operation_token.generate(); eprintln!("error in parsing mail\n{}", operation.description()); return None; - }, + } }; let mut mail = Envelope::new(operation_token); diff --git a/src/mailbox/email/parser.rs b/src/mailbox/email/parser.rs index 81720b9d6..8b03aca57 100644 --- a/src/mailbox/email/parser.rs +++ b/src/mailbox/email/parser.rs @@ -22,12 +22,12 @@ use std; use std::str::from_utf8; use base64; use chrono; -use nom::{le_u8, is_hex_digit}; -use nom::{IResult,Needed,ErrorKind}; +use nom::{is_hex_digit, le_u8}; +use nom::{ErrorKind, IResult, Needed}; use nom::{Compare, CompareResult}; -use encoding::{Encoding, DecoderTrap}; +use encoding::{DecoderTrap, Encoding}; -fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> { +fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8], u8> { if input.is_empty() || input.len() < 3 { IResult::Incomplete(Needed::Size(1)) } else if input[0] == b'=' && is_hex_digit(input[1]) && is_hex_digit(input[2]) { @@ -45,7 +45,7 @@ fn quoted_printable_byte(input: &[u8]) -> IResult<&[u8],u8> { } else { input[2] - 87 }; - IResult::Done(&input[3..], a*16+b) + IResult::Done(&input[3..], a * 16 + b) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } @@ -70,18 +70,15 @@ 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') || input[i+1] == b'\n') { - return match from_utf8(&input[0..i]) { - Ok(v) => { - IResult::Done(&input[(i+1)..], v) - }, - Err(_) => { - IResult::Error(error_code!(ErrorKind::Custom(43))) - }, - } - } else if i + 1 > input_len { - return IResult::Incomplete(Needed::Size(1)); - } + ((input[i + 1] != b' ' && input[i + 1] != b'\t') || input[i + 1] == b'\n') + { + return match from_utf8(&input[0..i]) { + Ok(v) => IResult::Done(&input[(i + 1)..], v), + Err(_) => IResult::Error(error_code!(ErrorKind::Custom(43))), + }; + } else if i + 1 > input_len { + return IResult::Incomplete(Needed::Size(1)); + } } } IResult::Error(error_code!(ErrorKind::Custom(43))) @@ -90,12 +87,13 @@ 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<&str>, map_res!(is_not!(":\n"), from_utf8)); /* Parse a single header as a tuple -> (&str, Vec<&str>) */ -named!(header<(&str, &str)>, - separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value))); +named!( + header<(&str, &str)>, + separated_pair!(complete!(name), ws!(tag!(":")), complete!(header_value)) +); /* Parse all headers -> Vec<(&str, Vec<&str>)> */ named!(pub headers>, many1!(complete!(header))); @@ -126,103 +124,95 @@ named!(pub attachment<(std::vec::Vec<(&str, &str)>, &[u8])>, /* TODO: make a map of encodings and decoding functions so that they can be reused and easily * extended */ -use encoding::all::{ISO_8859_1,ISO_8859_2, ISO_8859_7, WINDOWS_1253, GBK}; +use encoding::all::{ISO_8859_1, ISO_8859_2, ISO_8859_7, WINDOWS_1253, GBK}; fn encoded_word(input: &[u8]) -> IResult<&[u8], Vec> { if input.len() < 5 { return IResult::Incomplete(Needed::Unknown); } else if input[0] != b'=' || input[1] != b'?' { - return IResult::Error(error_code!(ErrorKind::Custom(43))) + return IResult::Error(error_code!(ErrorKind::Custom(43))); } - for tag in &["UTF-8", "iso-8859-7", "windows-1253", "iso-8859-1", "iso-8859-2", "gbk"] { + for tag in &[ + "UTF-8", + "iso-8859-7", + "windows-1253", + "iso-8859-1", + "iso-8859-2", + "gbk", + ] { if let CompareResult::Ok = (&input[2..]).compare_no_case(*tag) { let tag_len = tag.len(); /* tag must end with ?_? where _ is either Q or B, eg: =?UTF-8?B? */ - if input[2+tag_len] != b'?' || input[2+tag_len+2] != b'?' { - return IResult::Error(error_code!(ErrorKind::Custom(43))) + if input[2 + tag_len] != b'?' || input[2 + tag_len + 2] != b'?' { + return IResult::Error(error_code!(ErrorKind::Custom(43))); } /* See if input ends with "?=" and get ending index */ let mut encoded_idx = None; - for i in (5+tag_len)..input.len() { - if input[i] == b'?' && i < input.len() && input[i+1] == b'=' { + for i in (5 + tag_len)..input.len() { + if input[i] == b'?' && i < input.len() && input[i + 1] == b'=' { encoded_idx = Some(i); break; } - }; - if encoded_idx.is_none() { - return IResult::Error(error_code!(ErrorKind::Custom(43))) } - let encoded = &input[5+tag_len..encoded_idx.unwrap()]; + if encoded_idx.is_none() { + return IResult::Error(error_code!(ErrorKind::Custom(43))); + } + let encoded = &input[5 + tag_len..encoded_idx.unwrap()]; - let s:Vec = match input[2+tag_len+1] { - b'b' | b'B' => { - match base64::decode(encoded) { - Ok(v) => { - v - }, - Err(_) => { - encoded.to_vec() - }, - } + let s: Vec = match input[2 + tag_len + 1] { + b'b' | b'B' => match base64::decode(encoded) { + Ok(v) => v, + Err(_) => encoded.to_vec(), }, - b'q' | b'Q' => { - match get_quoted_printed_bytes(encoded) { - IResult::Done(b"", s) => { - s - }, - _ => { - return IResult::Error(error_code!(ErrorKind::Custom(43))) - }, - } - - }, - _ => { - return IResult::Error(error_code!(ErrorKind::Custom(43))) + b'q' | b'Q' => match get_quoted_printed_bytes(encoded) { + IResult::Done(b"", s) => s, + _ => return IResult::Error(error_code!(ErrorKind::Custom(43))), }, + _ => return IResult::Error(error_code!(ErrorKind::Custom(43))), }; match *tag { "UTF-8" => { - return IResult::Done(&input[encoded_idx.unwrap()+2..], s); - }, + return IResult::Done(&input[encoded_idx.unwrap() + 2..], s); + } "iso-8859-7" => { return if let Ok(v) = ISO_8859_7.decode(&s, DecoderTrap::Strict) { - IResult::Done(&input[encoded_idx.unwrap()+2..], v.into_bytes()) + IResult::Done(&input[encoded_idx.unwrap() + 2..], v.into_bytes()) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - }, + } "windows-1253" => { return if let Ok(v) = WINDOWS_1253.decode(&s, DecoderTrap::Strict) { - IResult::Done(&input[encoded_idx.unwrap()+2..], v.into_bytes()) + IResult::Done(&input[encoded_idx.unwrap() + 2..], v.into_bytes()) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - }, + } "iso-8859-1" => { return if let Ok(v) = ISO_8859_1.decode(&s, DecoderTrap::Strict) { - IResult::Done(&input[encoded_idx.unwrap()+2..], v.into_bytes()) + IResult::Done(&input[encoded_idx.unwrap() + 2..], v.into_bytes()) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - }, + } "iso-8859-2" => { return if let Ok(v) = ISO_8859_2.decode(&s, DecoderTrap::Strict) { - IResult::Done(&input[encoded_idx.unwrap()+2..], v.into_bytes()) + IResult::Done(&input[encoded_idx.unwrap() + 2..], v.into_bytes()) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - }, + } "gbk" => { return if let Ok(v) = GBK.decode(&s, DecoderTrap::Strict) { - IResult::Done(&input[encoded_idx.unwrap()+2..], v.into_bytes()) + IResult::Done(&input[encoded_idx.unwrap() + 2..], v.into_bytes()) } else { IResult::Error(error_code!(ErrorKind::Custom(43))) } - }, + } _ => { panic!(); - }, + } } } else { continue; @@ -232,10 +222,14 @@ fn encoded_word(input: &[u8]) -> IResult<&[u8], Vec> { IResult::Error(error_code!(ErrorKind::Custom(43))) } -named!(qp_underscore_header, - do_parse!(tag!("_") >> ( { b' ' } ))); +named!(qp_underscore_header, do_parse!(tag!("_") >> ({ b' ' }))); -named!(get_quoted_printed_bytes>, many0!(alt_complete!(quoted_printable_byte | qp_underscore_header | le_u8))); +named!( + get_quoted_printed_bytes>, + many0!(alt_complete!( + quoted_printable_byte | qp_underscore_header | le_u8 + )) +); named!(encoded_word_list, ws!(do_parse!( list: separated_nonempty_list!(complete!(is_a!(" \n\r\t")), encoded_word) >> @@ -273,41 +267,79 @@ named!(pub subject, ws!(do_parse!( #[test] fn test_subject() { let subject_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()); + assert_eq!( + ( + &b""[..], + "list.free.de mailing list memberships reminder".to_string() + ), + subject(subject_s).unwrap() + ); let subject_s = "=?UTF-8?B?zp3Orc6/IM+Az4HOv8+Dz4nPgM65zrrPjCDOvM6uzr3Phc68zrEgzrHPhs6v?= =?UTF-8?B?z4fOuM63?=".as_bytes(); - assert_eq!((&b""[..], "Νέο προσωπικό μήνυμα αφίχθη".to_string()), subject(subject_s).unwrap()); + assert_eq!( + ( + &b""[..], + "Νέο προσωπικό μήνυμα αφίχθη".to_string() + ), + subject(subject_s).unwrap() + ); let subject_s = "=?utf-8?B?bW9vZGxlOiDOsc69zrHPg866z4zPgM63z4POtyDOv868zqzOtM6xz4Igz4M=?= =?utf-8?B?z4XOts63z4TOrs+DzrXPic69?=".as_bytes(); - assert_eq!((&b""[..], "moodle: ανασκόπηση ομάδας συζητήσεων".to_string()), subject(subject_s).unwrap()); + assert_eq!( + ( + &b""[..], + "moodle: ανασκόπηση ομάδας συζητήσεων".to_string() + ), + subject(subject_s).unwrap() + ); - let subject_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()); + let subject_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() + ); let subject_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()); + assert_eq!( + "Πρόσθε".to_string(), + from_utf8(&encoded_word(subject_s).to_full_result().unwrap()).unwrap() + ); let subject_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()); + assert_eq!( + "Re: Πρόβλημα με την αυριανή βαρδια".to_string(), + from_utf8(&encoded_word(subject_s).to_full_result().unwrap()).unwrap() + ); let subject_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(); - assert_eq!((&b""[..], "Πρόσθετη εξεταστική".to_string()), subject(subject_s).unwrap()); + =?UTF-8?Q?=CE=B1=CF=83=CF=84=CE=B9=CE=BA=CE=AE?=" + .as_bytes(); + assert_eq!( + ( + &b""[..], + "Πρόσθετη εξεταστική".to_string() + ), + subject(subject_s).unwrap() + ); } fn eat_comments(input: &str) -> String { let mut in_comment = false; - input.chars().fold(String::with_capacity(input.len()), |mut acc, x| { - if x == '(' && !in_comment { - in_comment = true; - acc - } else if x == ')' && in_comment { - in_comment = false; - acc - } else if in_comment { - acc - } else { - acc.push(x); acc - } - }) + input + .chars() + .fold(String::with_capacity(input.len()), |mut acc, x| { + if x == '(' && !in_comment { + in_comment = true; + acc + } else if x == ')' && in_comment { + in_comment = false; + acc + } else if in_comment { + acc + } else { + acc.push(x); + acc + } + }) } #[test] @@ -317,13 +349,16 @@ fn test_eat_comments() { let s = "Thu, 31 Aug 2017 13:43:37 +0000 (UTC)"; assert_eq!(eat_comments(s), "Thu, 31 Aug 2017 13:43:37 +0000 "); } -/* +/* * Date should tokenize input and convert the tokens, - * right now we expect input will have no extra spaces in between tokens + * 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> { - let parsed_result = subject(eat_comments(input).as_bytes()).to_full_result().unwrap().replace("-", "+"); + let parsed_result = subject(eat_comments(input).as_bytes()) + .to_full_result() + .unwrap() + .replace("-", "+"); chrono::DateTime::parse_from_rfc2822(parsed_result.trim()).ok() } @@ -340,16 +375,16 @@ named!(pub message_id<&str>, map_res!(complete!(delimited!(tag!("<"), take_until1!(">"), tag!(">"))), from_utf8) ); -fn message_id_peek(input: &[u8]) -> IResult<&[u8],&str> { +fn message_id_peek(input: &[u8]) -> IResult<&[u8], &str> { let input_length = input.len(); - if input.is_empty() { + if input.is_empty() { IResult::Incomplete(Needed::Size(1)) - } else if input_length == 2 || input[0] != b'<' { + } else if input_length == 2 || input[0] != b'<' { IResult::Error(error_code!(ErrorKind::Custom(43))) } 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..], from_utf8(&input[0..i + 1]).unwrap()); } } IResult::Incomplete(Needed::Unknown) @@ -381,19 +416,24 @@ named_args!(pub attachments<'a>(boundary: &'a str, boundary_end: &'a str) < Vec< fn test_attachments() { use std::io::Read; let mut buffer: Vec = Vec::new(); - let _ = std::fs::File::open("test/attachment_test").unwrap().read_to_end(&mut buffer); + let _ = std::fs::File::open("test/attachment_test") + .unwrap() + .read_to_end(&mut buffer); let boundary = "--b1_4382d284f0c601a737bb32aaeda53160--"; let boundary_len = boundary.len(); let (_, body) = match mail(&buffer).to_full_result() { Ok(v) => v, - Err(_) => { panic!() } - }; - let attachments = attachments(body, &boundary[0..boundary_len-2], &boundary).to_full_result().unwrap(); + Err(_) => panic!(), + }; + let attachments = attachments(body, &boundary[0..boundary_len - 2], &boundary) + .to_full_result() + .unwrap(); assert_eq!(attachments.len(), 4); } -named!(content_type_parameter< (&str, &str) >, - do_parse!( +named!( + content_type_parameter<(&str, &str)>, + do_parse!( tag!(";") >> name: terminated!(map_res!(ws!(take_until!("=")), from_utf8), tag!("=")) >> value: map_res!(ws!( @@ -402,7 +442,8 @@ named!(content_type_parameter< (&str, &str) >, ( { (name, value) } ) - )); + ) +); named!(pub content_type< (&str, &str, Vec<(&str, &str)>) >, diff --git a/src/mailbox/mod.rs b/src/mailbox/mod.rs index 6ceec1e24..37f46055e 100644 --- a/src/mailbox/mod.rs +++ b/src/mailbox/mod.rs @@ -29,14 +29,14 @@ use error::Result; pub mod accounts; pub use mailbox::accounts::Account; mod thread; -use mailbox::thread::{Container, build_threads}; +use mailbox::thread::{build_threads, Container}; use std::option::Option; /*a Mailbox represents a folder of mail. Currently only Maildir is supported.*/ -#[derive(Debug,Clone)] -pub struct Mailbox{ +#[derive(Debug, Clone)] +pub struct Mailbox { pub path: String, pub collection: Vec, pub threaded_collection: Vec, @@ -45,8 +45,7 @@ pub struct Mailbox{ } -impl Mailbox -{ +impl Mailbox { pub fn new(path: &str, sent_folder: &Option>) -> Result { let mut collection: Vec = maildir::MaildirType::new(path).get()?; collection.sort_by(|a, b| a.get_date().cmp(&b.get_date())); @@ -70,12 +69,11 @@ impl Mailbox thread.get_message().unwrap() } pub fn get_mail_and_thread(&mut self, i: usize) -> (&mut Envelope, Container) { - let x = &mut self.collection.as_mut_slice()[i]; - let thread = self.threads[x.get_thread()]; - (x, thread) + let x = &mut self.collection.as_mut_slice()[i]; + let thread = self.threads[x.get_thread()]; + (x, thread) } pub fn get_thread(&self, i: usize) -> &Container { &self.threads[i] } } - diff --git a/src/mailbox/thread.rs b/src/mailbox/thread.rs index 1e931e5d2..bb8021a97 100644 --- a/src/mailbox/thread.rs +++ b/src/mailbox/thread.rs @@ -103,7 +103,7 @@ impl Container { fn set_show_subject(&mut self, set: bool) -> () { self.show_subject = set; } - pub fn get_show_subject(&self) -> bool { + pub fn get_show_subject(&self) -> bool { self.show_subject } } @@ -111,18 +111,17 @@ impl Container { impl PartialEq for Container { fn eq(&self, other: &Container) -> bool { match (self.message, other.message) { - (Some(s), Some(o)) => { - s == o - }, - _ => { - self.id == other.id - } + (Some(s), Some(o)) => s == o, + _ => self.id == other.id, } } } -fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, collection: &mut [Envelope]) -> () -{ +fn build_collection( + threads: &mut Vec, + id_table: &mut FnvHashMap, + collection: &mut [Envelope], +) -> () { for (i, x) in collection.iter_mut().enumerate() { let x_index; /* x's index in threads */ let m_id = x.get_message_id_raw().to_string(); @@ -142,17 +141,16 @@ fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, id_table: &mut FnvHashMap, id_table: &mut FnvHashMap { parent_iter = p; }, - None => { break 'date; }, + Some(p) => { + parent_iter = p; + } + None => { + break 'date; + } } } curr_ref = parent_id; @@ -229,167 +230,196 @@ fn build_collection(threads: &mut Vec, id_table: &mut FnvHashMap, sent_folder: &Option>) -> (Vec, Vec) { - /* To reconstruct thread information from the mails we need: */ +pub fn build_threads( + collection: &mut Vec, + sent_folder: &Option>, +) -> (Vec, Vec) { + /* To reconstruct thread information from the mails we need: */ - /* a vector to hold thread members */ - let mut threads: Vec = Vec::with_capacity((collection.len() as f64 * 1.2) as usize); - /* A hash table of Message IDs */ - let mut id_table: FnvHashMap = FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); + /* a vector to hold thread members */ + let mut threads: Vec = Vec::with_capacity((collection.len() as f64 * 1.2) as usize); + /* A hash table of Message IDs */ + let mut id_table: FnvHashMap = + FnvHashMap::with_capacity_and_hasher(collection.len(), Default::default()); - /* Add each message to id_table and threads, and link them together according to the - * References / In-Reply-To headers */ - build_collection(&mut threads, &mut id_table, collection); - let mut idx = collection.len(); - let mut tidx = threads.len(); - /* Link messages from Sent folder if they are relevant to this folder. - * This means that - * - if a message from Sent is a reply to a message in this folder, we will - * add it to the threading (but not the collection; non-threading users shouldn't care - * about this) - * - if a message in this folder is a reply to a message we sent, we will add it to the - * threading - */ + /* Add each message to id_table and threads, and link them together according to the + * References / In-Reply-To headers */ + build_collection(&mut threads, &mut id_table, collection); + let mut idx = collection.len(); + let mut tidx = threads.len(); + /* Link messages from Sent folder if they are relevant to this folder. + * This means that + * - if a message from Sent is a reply to a message in this folder, we will + * add it to the threading (but not the collection; non-threading users shouldn't care + * about this) + * - if a message in this folder is a reply to a message we sent, we will add it to the + * threading + */ - if let Some(ref sent_box) = *sent_folder { - if sent_box.is_ok() { - let sent_mailbox = sent_box.as_ref(); - let sent_mailbox = sent_mailbox.unwrap();; + if let Some(ref sent_box) = *sent_folder { + if sent_box.is_ok() { + let sent_mailbox = sent_box.as_ref(); + let sent_mailbox = sent_mailbox.unwrap(); - for x in &sent_mailbox.collection { - if id_table.contains_key(x.get_message_id_raw()) || - (!x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw())) { - let mut x: Envelope = (*x).clone(); - if id_table.contains_key(x.get_message_id_raw()) { - let c = id_table[x.get_message_id_raw()]; - if threads[c].message.is_some() { - /* skip duplicate message-id, but this should be handled instead */ - continue; + for x in &sent_mailbox.collection { + if id_table.contains_key(x.get_message_id_raw()) || + (!x.get_in_reply_to_raw().is_empty() && + id_table.contains_key(x.get_in_reply_to_raw())) + { + let mut x: Envelope = (*x).clone(); + if id_table.contains_key(x.get_message_id_raw()) { + let c = id_table[x.get_message_id_raw()]; + if threads[c].message.is_some() { + /* skip duplicate message-id, but this should be handled instead */ + continue; + } + threads[c].message = Some(idx); + assert!(threads[c].has_children()); + threads[c].date = x.get_date(); + x.set_thread(c); + } else if !x.get_in_reply_to_raw().is_empty() && + id_table.contains_key(x.get_in_reply_to_raw()) + { + let p = id_table[x.get_in_reply_to_raw()]; + let c = if id_table.contains_key(x.get_message_id_raw()) { + id_table[x.get_message_id_raw()] + } else { + threads.push(Container { + message: Some(idx), + id: tidx, + parent: Some(p), + first_child: None, + next_sibling: None, + date: x.get_date(), + indentation: 0, + show_subject: true, + }); + id_table.insert(x.get_message_id_raw().to_string(), tidx); + x.set_thread(tidx); + tidx += 1; + tidx - 1 + }; + threads[c].parent = Some(p); + if threads[p].is_descendant(&threads, &threads[c]) || + threads[c].is_descendant(&threads, &threads[p]) + { + continue; + } + if threads[p].first_child.is_none() { + threads[p].first_child = Some(c); + } else { + let mut fc = threads[p].first_child.unwrap(); + while threads[fc].next_sibling.is_some() { + threads[fc].parent = Some(p); + fc = threads[fc].next_sibling.unwrap(); + } + threads[fc].next_sibling = Some(c); + threads[fc].parent = Some(p); + } + /* update thread date */ + let mut parent_iter = p; + 'date: loop { + let p = &mut threads[parent_iter]; + if p.date < x.get_date() { + p.date = x.get_date(); + } + match p.parent { + Some(p) => { + parent_iter = p; } - threads[c].message = Some(idx); - assert!(threads[c].has_children()); - threads[c].date = x.get_date(); - x.set_thread(c); - } else if !x.get_in_reply_to_raw().is_empty() && id_table.contains_key(x.get_in_reply_to_raw()) { - let p = id_table[x.get_in_reply_to_raw()]; - let c = if id_table.contains_key(x.get_message_id_raw()) { - id_table[x.get_message_id_raw()] - } else { - threads.push( - Container { - message: Some(idx), - id: tidx, - parent: Some(p), - first_child: None, - next_sibling: None, - date: x.get_date(), - indentation: 0, - show_subject: true, - }); - id_table.insert(x.get_message_id_raw().to_string(), tidx); - x.set_thread(tidx); - tidx += 1; - tidx - 1 - }; - threads[c].parent = Some(p); - if threads[p].is_descendant(&threads, &threads[c]) || - threads[c].is_descendant(&threads, &threads[p]) { - continue; - } - if threads[p].first_child.is_none() { - threads[p].first_child = Some(c); - } else { - let mut fc = threads[p].first_child.unwrap(); - while threads[fc].next_sibling.is_some() { - threads[fc].parent = Some(p); - fc = threads[fc].next_sibling.unwrap(); - } - threads[fc].next_sibling = Some(c); - threads[fc].parent = Some(p); - } - /* update thread date */ - let mut parent_iter = p; - 'date: loop { - let p = &mut threads[parent_iter]; - if p.date < x.get_date() { - p.date = x.get_date(); - } - match p.parent { - Some(p) => { parent_iter = p; }, - None => { break 'date; }, - } + None => { + break 'date; } } - collection.push(x); - idx += 1; } + } + collection.push(x); + idx += 1; } } } - /* Walk over the elements of id_table, and gather a list of the Container objects that have - * no parents. These are the root messages of each thread */ - let mut root_set = Vec::with_capacity(collection.len()); - 'root_set: for v in id_table.values() { - if threads[*v].parent.is_none() { - if !threads[*v].has_message() && threads[*v].has_children() && !threads[threads[*v].first_child.unwrap()].has_sibling() { - /* Do not promote the children if doing so would promote them to the root set - * -- unless there is only one child, in which case, do. */ - root_set.push(threads[*v].first_child.unwrap()); - continue 'root_set; - } - root_set.push(*v); - } - } - root_set.sort_by(|a, b| threads[*b].date.cmp(&threads[*a].date)); - - /* Group messages together by thread in a collection so we can print them together */ - let mut threaded_collection: Vec = Vec::with_capacity(collection.len()); - fn build_threaded(threads: &mut Vec, indentation: usize, threaded: &mut Vec, i: usize, root_subject_idx: usize, collection: &[Envelope]) + } + /* Walk over the elements of id_table, and gather a list of the Container objects that have + * no parents. These are the root messages of each thread */ + let mut root_set = Vec::with_capacity(collection.len()); + 'root_set: for v in id_table.values() { + if threads[*v].parent.is_none() { + if !threads[*v].has_message() && threads[*v].has_children() && + !threads[threads[*v].first_child.unwrap()].has_sibling() { - let thread = threads[i]; - if threads[root_subject_idx].has_message() { - let root_subject = collection[threads[root_subject_idx].get_message().unwrap()].get_subject(); - /* If the Container has no Message, but does have children, remove this container but - * promote its children to this level (that is, splice them in to the current child - * list.) */ - if indentation > 0 && thread.has_message() { - let subject = collection[thread.get_message().unwrap()].get_subject(); - if subject == root_subject || subject.starts_with("Re: ") && subject.ends_with(root_subject) { - threads[i].set_show_subject(false); - } - } + /* Do not promote the children if doing so would promote them to the root set + * -- unless there is only one child, in which case, do. */ + root_set.push(threads[*v].first_child.unwrap()); + continue 'root_set; } - if thread.has_parent() && !threads[thread.get_parent().unwrap()].has_message() { - threads[i].parent = None; - } - let indentation = - if thread.has_message() { - threads[i].set_indentation(indentation); - if !threaded.contains(&i) { - threaded.push(i); - } - indentation + 1 - } else if indentation > 0 { - indentation - } else { - indentation + 1 - }; - if thread.has_children() { - let mut fc = thread.get_first_child().unwrap(); - loop { - build_threaded(threads, indentation, threaded, fc, i, collection); - let thread_ = threads[fc]; - if !thread_.has_sibling() { - break; - } - fc = thread_.get_next_sibling().unwrap(); + root_set.push(*v); + } + } + root_set.sort_by(|a, b| threads[*b].date.cmp(&threads[*a].date)); + + /* Group messages together by thread in a collection so we can print them together */ + let mut threaded_collection: Vec = Vec::with_capacity(collection.len()); + fn build_threaded( + threads: &mut Vec, + indentation: usize, + threaded: &mut Vec, + i: usize, + root_subject_idx: usize, + collection: &[Envelope], + ) { + let thread = threads[i]; + if threads[root_subject_idx].has_message() { + let root_subject = + collection[threads[root_subject_idx].get_message().unwrap()].get_subject(); + /* If the Container has no Message, but does have children, remove this container but + * promote its children to this level (that is, splice them in to the current child + * list.) */ + if indentation > 0 && thread.has_message() { + let subject = collection[thread.get_message().unwrap()].get_subject(); + if subject == root_subject || + subject.starts_with("Re: ") && subject.ends_with(root_subject) + { + threads[i].set_show_subject(false); } } } - for i in &root_set { - build_threaded(&mut threads, 0, &mut threaded_collection, *i, *i, collection); + if thread.has_parent() && !threads[thread.get_parent().unwrap()].has_message() { + threads[i].parent = None; } + let indentation = if thread.has_message() { + threads[i].set_indentation(indentation); + if !threaded.contains(&i) { + threaded.push(i); + } + indentation + 1 + } else if indentation > 0 { + indentation + } else { + indentation + 1 + }; + if thread.has_children() { + let mut fc = thread.get_first_child().unwrap(); + loop { + build_threaded(threads, indentation, threaded, fc, i, collection); + let thread_ = threads[fc]; + if !thread_.has_sibling() { + break; + } + fc = thread_.get_next_sibling().unwrap(); + } + } + } + for i in &root_set { + build_threaded( + &mut threads, + 0, + &mut threaded_collection, + *i, + *i, + collection, + ); + } - (threads, threaded_collection) + (threads, threaded_collection) } diff --git a/src/ui/index.rs b/src/ui/index.rs index 69cd703e3..456b65e13 100644 --- a/src/ui/index.rs +++ b/src/ui/index.rs @@ -46,10 +46,7 @@ impl Window for ErrorWindow { ncurses::waddstr(self.win, &self.description); ncurses::wrefresh(self.win); } - fn handle_input(&mut self, _: i32) -> () { - - - } + fn handle_input(&mut self, _: i32) -> () {} } impl ErrorWindow { pub fn new(err: MeliError) -> Self { @@ -80,7 +77,6 @@ pub struct Index { /* threading */ threaded: bool, - cursor_idx: usize, } @@ -97,9 +93,13 @@ impl Window for Index { //ncurses::wclear(self.pad); if self.threaded { - let mut indentations : Vec = Vec::with_capacity(6); + let mut indentations: Vec = Vec::with_capacity(6); /* Draw threaded view. */ - let mut iter = self.mailbox.threaded_collection.iter().enumerate().peekable(); + let mut iter = self.mailbox + .threaded_collection + .iter() + .enumerate() + .peekable(); /* This is just a desugared for loop so that we can use .peek() */ while let Some((idx, i)) = iter.next() { let container = self.mailbox.get_thread(*i); @@ -107,10 +107,12 @@ impl Window for Index { assert_eq!(container.has_message(), true); match iter.peek() { - Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() == indentation => { + Some(&(_, x)) + if self.mailbox.get_thread(*x).get_indentation() == indentation => + { indentations.pop(); indentations.push(true); - }, + } _ => { indentations.pop(); indentations.push(false); @@ -119,23 +121,36 @@ impl Window for Index { if container.has_sibling() { indentations.pop(); indentations.push(true); - } + } let x = &self.mailbox.collection[container.get_message().unwrap()]; - Index::draw_entry(self.pad, x, idx, indentation, container.has_sibling(), container.has_parent(), idx == self.cursor_idx, container.get_show_subject(), Some(&indentations)); + Index::draw_entry( + self.pad, + x, + idx, + indentation, + container.has_sibling(), + container.has_parent(), + idx == self.cursor_idx, + container.get_show_subject(), + Some(&indentations), + ); match iter.peek() { - Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() > indentation => { + Some(&(_, x)) + if self.mailbox.get_thread(*x).get_indentation() > indentation => + { indentations.push(false); - }, - Some(&(_, x)) if self.mailbox.get_thread(*x).get_indentation() < indentation => { + } + Some(&(_, x)) + if self.mailbox.get_thread(*x).get_indentation() < indentation => + { for _ in 0..(indentation - self.mailbox.get_thread(*x).get_indentation()) { indentations.pop(); } - }, - _ => { } + _ => {} } } - /* + /* for (idx, i) in self.mailbox.threaded_collection.iter().enumerate() { let container = self.mailbox.get_thread(*i); @@ -155,7 +170,17 @@ impl Window for Index { */ } else { for (idx, x) in self.mailbox.collection.as_mut_slice().iter().enumerate() { - Index::draw_entry(self.pad, x, idx, 0, false, false, idx == self.cursor_idx, true, None); + Index::draw_entry( + self.pad, + x, + idx, + 0, + false, + false, + idx == self.cursor_idx, + true, + None, + ); } } ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width); @@ -176,7 +201,7 @@ impl Window for Index { ncurses::doupdate(); if self.mailbox.get_length() == 0 { return; - } + } /* Draw newly highlighted entry */ ncurses::wmove(self.pad, self.cursor_idx as i32, 0); let pair = super::COLOR_PAIR_CURSOR; @@ -212,24 +237,20 @@ impl Window for Index { ncurses::getbegyx(self.win, &mut y, &mut x); let prev_idx = self.cursor_idx; match motion { - ncurses::KEY_UP => { - if self.cursor_idx > 0 { - self.cursor_idx -= 1; - } else { - return; - } + ncurses::KEY_UP => if self.cursor_idx > 0 { + self.cursor_idx -= 1; + } else { + return; }, - ncurses::KEY_DOWN => { - if self.cursor_idx < self.mailbox.get_length() - 1 { - self.cursor_idx += 1; - } else { - return; - } + ncurses::KEY_DOWN => if self.cursor_idx < self.mailbox.get_length() - 1 { + self.cursor_idx += 1; + } else { + return; }, 10 => { self.show_pager(); self.redraw(); - }, + } _ => { return; } @@ -257,21 +278,23 @@ impl Window for Index { let mut x = 32; loop { match ncurses::mvwinch(self.pad, prev_idx as i32, x) & ncurses::A_CHARTEXT() { - 32 => { /* ASCII code for space */ + 32 => { + /* ASCII code for space */ ncurses::wchgat(self.pad, x, 0, pair); - }, - 62 => { /* ASCII code for '>' */ + } + 62 => { + /* ASCII code for '>' */ ncurses::wchgat(self.pad, x, 0, super::COLOR_PAIR_THREAD_INDENT); ncurses::wmove(self.pad, prev_idx as i32, x + 1); break; } _ => { ncurses::wchgat(self.pad, x, 0, super::COLOR_PAIR_THREAD_INDENT); - }, + } } x += 1; } - } + } ncurses::wchgat(self.pad, -1, 0, pair); } @@ -295,7 +318,8 @@ impl Window for Index { * └ i-1 ┘ */ if pminrow != pminrow_prev && - pminrow + self.screen_height > self.mailbox.get_length() as i32 { + pminrow + self.screen_height > self.mailbox.get_length() as i32 + { /* touch dead entries in index (tell ncurses to redraw the empty lines next refresh) */ let live_entries = self.mailbox.get_length() as i32 - pminrow; ncurses::wredrawln(self.win, live_entries, self.screen_height); @@ -356,26 +380,37 @@ impl Index { /* draw_entry() doesn't take &mut self because borrow checker complains if it's called from * another method. */ - fn draw_entry(win: ncurses::WINDOW, mail: &Envelope, i: usize, indent: usize, - has_sibling: bool, has_parent: bool, highlight: bool, - show_subject: bool, indentations: Option<&Vec>) { + fn draw_entry( + win: ncurses::WINDOW, + mail: &Envelope, + i: usize, + indent: usize, + has_sibling: bool, + has_parent: bool, + highlight: bool, + show_subject: bool, + indentations: Option<&Vec>, + ) { /* TODO: use addchstr */ - let pair = - if highlight { - super::COLOR_PAIR_CURSOR - } else if i % 2 == 0 { - super::COLOR_PAIR_THREAD_EVEN - } else { - super::COLOR_PAIR_THREAD_ODD - }; + let pair = if highlight { + super::COLOR_PAIR_CURSOR + } else if i % 2 == 0 { + super::COLOR_PAIR_THREAD_EVEN + } else { + super::COLOR_PAIR_THREAD_ODD + }; let attr = ncurses::COLOR_PAIR(pair); ncurses::wattron(win, attr); ncurses::waddstr(win, &format!("{}\t", i)); - ncurses::waddstr(win, &mail.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string()); + ncurses::waddstr( + win, + &mail.get_datetime().format("%Y-%m-%d %H:%M:%S").to_string(), + ); ncurses::waddch(win, '\t' as u64); for i in 0..indent { - if indentations.is_some() && indentations.unwrap().len() > i && indentations.unwrap()[i] { + if indentations.is_some() && indentations.unwrap().len() > i && indentations.unwrap()[i] + { ncurses::wattron(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_THREAD_INDENT)); ncurses::waddstr(win, "│"); ncurses::wattroff(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_THREAD_INDENT)); @@ -401,7 +436,7 @@ impl Index { } ncurses::wattron(win, attr); if show_subject { - ncurses::waddstr(win, &format!("{:.85}",mail.get_subject())); + ncurses::waddstr(win, &format!("{:.85}", mail.get_subject())); /* if indent == 0 { if mail.get_subject().chars().count() < 85 { @@ -426,8 +461,7 @@ impl Index { if self.mailbox.get_length() == 0 { return; } - ncurses::getmaxyx(self.win, - &mut self.screen_height, &mut self.screen_width); + ncurses::getmaxyx(self.win, &mut self.screen_height, &mut self.screen_width); let x: &mut Envelope = if self.threaded { let i = self.mailbox.get_threaded_mail(self.cursor_idx); &mut self.mailbox.collection[i] @@ -452,7 +486,7 @@ impl Index { } } } -impl Drop for Index { +impl Drop for Index { fn drop(&mut self) { ncurses::delwin(self.pad); ncurses::wclear(self.win); diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 6e123a140..7aaad4adc 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -55,7 +55,7 @@ impl TUI { /* Set the window's background color. */ ncurses::bkgd( ' ' as ncurses::chtype | ncurses::COLOR_PAIR(COLOR_PAIR_DEFAULT) as ncurses::chtype, - ); + ); TUI {} } } diff --git a/src/ui/pager.rs b/src/ui/pager.rs index 9cff7a0b4..27871094e 100644 --- a/src/ui/pager.rs +++ b/src/ui/pager.rs @@ -35,8 +35,7 @@ pub struct Pager { } impl Pager { - pub fn new(parent: ncurses::WINDOW, - entry: &mut mailbox::Envelope) -> Pager { + pub fn new(parent: ncurses::WINDOW, entry: &mut mailbox::Envelope) -> Pager { let mut screen_height = 0; let mut screen_width = 0; ncurses::getmaxyx(parent, &mut screen_height, &mut screen_width); @@ -85,16 +84,13 @@ impl Pager { return; } match motion { - ncurses::KEY_UP => { - if self.curr_y > 0 { - self.curr_y -= 1; - } - } - ncurses::KEY_DOWN => { - if self.curr_y < self.rows && self.rows - self.curr_y > pager_size { - self.curr_y += 1; - } - } + ncurses::KEY_UP => if self.curr_y > 0 { + self.curr_y -= 1; + }, + ncurses::KEY_DOWN => if self.curr_y < self.rows && self.rows - self.curr_y > pager_size + { + self.curr_y += 1; + }, ncurses::KEY_NPAGE => { if self.curr_y + h < self.rows && self.rows - self.curr_y - h > pager_size { self.curr_y += pager_size; @@ -106,13 +102,11 @@ impl Pager { }; } } - ncurses::KEY_PPAGE => { - if self.curr_y >= pager_size { - self.curr_y -= pager_size; - } else { - self.curr_y = 0 - } - } + ncurses::KEY_PPAGE => if self.curr_y >= pager_size { + self.curr_y -= pager_size; + } else { + self.curr_y = 0 + }, _ => {} } /* @@ -140,31 +134,19 @@ impl Pager { let mut i = 0; ncurses::wattron(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_HEADERS)); ncurses::waddstr(win, "Date: "); - ncurses::waddstr( - win, - mail.get_date_as_str() - ); + ncurses::waddstr(win, mail.get_date_as_str()); ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "From: "); - ncurses::waddstr( - win, - mail.get_from(), - ); + ncurses::waddstr(win, mail.get_from()); ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "To: "); - ncurses::waddstr( - win, - mail.get_to(), - ); + ncurses::waddstr(win, mail.get_to()); ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "Subject: "); - ncurses::waddstr( - win, - mail.get_subject(), - ); + ncurses::waddstr(win, mail.get_subject()); ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "Message-ID: "); @@ -176,17 +158,11 @@ impl Pager { ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "References: "); - ncurses::waddstr( - win, - &format!("{:?}", mail.get_references()), - ); + ncurses::waddstr(win, &format!("{:?}", mail.get_references())); ncurses::waddstr(win, "\n"); i += 1; ncurses::waddstr(win, "In-Reply-To: "); - ncurses::waddstr( - win, - mail.get_in_reply_to_raw(), - ); + ncurses::waddstr(win, mail.get_in_reply_to_raw()); ncurses::waddstr(win, "\n"); i += 1; ncurses::wattroff(win, ncurses::COLOR_PAIR(super::COLOR_PAIR_HEADERS)); @@ -196,7 +172,8 @@ impl Pager { fn print_entry_content( win: ncurses::WINDOW, mail: &mut mailbox::Envelope, - height: i32) -> (ncurses::WINDOW, i32, i32) { + height: i32, + ) -> (ncurses::WINDOW, i32, i32) { let mut h = 0; let mut w = 0; /* height and width of self.win, the pager window */ @@ -231,7 +208,8 @@ impl Pager { } fn print_entry( win: ncurses::WINDOW, - mail: &mut mailbox::Envelope) -> (ncurses::WINDOW, i32, i32) { + mail: &mut mailbox::Envelope, + ) -> (ncurses::WINDOW, i32, i32) { let header_height = Pager::print_entry_headers(win, mail); Pager::print_entry_content(win, mail, header_height + 2) }