fix clippy lints (#255)

This commit is contained in:
Ethiraric 2021-11-03 10:11:54 +01:00 committed by GitHub
parent 52b55ac394
commit 9b0a3fcba5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 120 additions and 155 deletions

View file

@ -51,7 +51,7 @@ impl Account {
let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char)); let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char));
if name.is_empty() { if name.is_empty() {
format!("{}", self.email) self.email.clone()
} else if has_special_chars { } else if has_special_chars {
// so the name has special characters => Wrap it with '"' // so the name has special characters => Wrap it with '"'
format!("\"{}\" <{}>", name, self.email) format!("\"{}\" <{}>", name, self.email)
@ -112,7 +112,7 @@ impl<'a> TryFrom<(&'a Config, Option<&str>)> for Account {
.and_then(|dir| shellexpand::full(dir).ok()) .and_then(|dir| shellexpand::full(dir).ok())
.map(|dir| PathBuf::from(dir.to_string())) .map(|dir| PathBuf::from(dir.to_string()))
}) })
.unwrap_or_else(|| env::temp_dir()); .unwrap_or_else(env::temp_dir);
let default_page_size = account let default_page_size = account
.default_page_size .default_page_size
@ -146,24 +146,21 @@ impl<'a> TryFrom<(&'a Config, Option<&str>)> for Account {
default_page_size, default_page_size,
inbox_folder: account inbox_folder: account
.inbox_folder .inbox_folder
.as_ref() .as_deref()
.map(|s| s.as_str()) .or_else(|| config.inbox_folder.as_deref())
.or(config.inbox_folder.as_ref().map(|s| s.as_str())) .unwrap_or(DEFAULT_INBOX_FOLDER)
.unwrap_or(&DEFAULT_INBOX_FOLDER)
.to_string(), .to_string(),
sent_folder: account sent_folder: account
.sent_folder .sent_folder
.as_ref() .as_deref()
.map(|s| s.as_str()) .or_else(|| config.sent_folder.as_deref())
.or(config.sent_folder.as_ref().map(|s| s.as_str())) .unwrap_or(DEFAULT_SENT_FOLDER)
.unwrap_or(&DEFAULT_SENT_FOLDER)
.to_string(), .to_string(),
draft_folder: account draft_folder: account
.draft_folder .draft_folder
.as_ref() .as_deref()
.map(|s| s.as_str()) .or_else(|| config.draft_folder.as_deref())
.or(config.draft_folder.as_ref().map(|s| s.as_str())) .unwrap_or(DEFAULT_DRAFT_FOLDER)
.unwrap_or(&DEFAULT_DRAFT_FOLDER)
.to_string(), .to_string(),
watch_cmds: account watch_cmds: account
.watch_cmds .watch_cmds

View file

@ -12,7 +12,7 @@ pub fn notify<'a, ImapService: ImapServiceInterface<'a>>(
config: &Config, config: &Config,
imap: &mut ImapService, imap: &mut ImapService,
) -> Result<()> { ) -> Result<()> {
imap.notify(&config, keepalive) imap.notify(config, keepalive)
} }
/// Watch handler. /// Watch handler.

View file

@ -8,7 +8,6 @@ use native_tls::{TlsConnector, TlsStream};
use std::{ use std::{
collections::HashSet, collections::HashSet,
convert::{TryFrom, TryInto}, convert::{TryFrom, TryInto},
iter::FromIterator,
net::TcpStream, net::TcpStream,
}; };
@ -58,7 +57,7 @@ pub struct ImapService<'a> {
impl<'a> ImapService<'a> { impl<'a> ImapService<'a> {
fn sess(&mut self) -> Result<&mut ImapSession> { fn sess(&mut self) -> Result<&mut ImapSession> {
if let None = self.sess { if self.sess.is_none() {
debug!("create TLS builder"); debug!("create TLS builder");
debug!("insecure: {}", self.account.imap_insecure); debug!("insecure: {}", self.account.imap_insecure);
let builder = TlsConnector::builder() let builder = TlsConnector::builder()
@ -148,7 +147,7 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
.fetch(range, "(ENVELOPE FLAGS INTERNALDATE)") .fetch(range, "(ENVELOPE FLAGS INTERNALDATE)")
.context(r#"cannot fetch messages within range "{}""#)?; .context(r#"cannot fetch messages within range "{}""#)?;
self._raw_msgs_cache = Some(fetches); self._raw_msgs_cache = Some(fetches);
Ok(Envelopes::try_from(self._raw_msgs_cache.as_ref().unwrap())?) Envelopes::try_from(self._raw_msgs_cache.as_ref().unwrap())
} }
fn fetch_envelopes_with( fn fetch_envelopes_with(
@ -186,7 +185,7 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
.fetch(&range, "(ENVELOPE FLAGS INTERNALDATE)") .fetch(&range, "(ENVELOPE FLAGS INTERNALDATE)")
.context(r#"cannot fetch messages within range "{}""#)?; .context(r#"cannot fetch messages within range "{}""#)?;
self._raw_msgs_cache = Some(fetches); self._raw_msgs_cache = Some(fetches);
Ok(Envelopes::try_from(self._raw_msgs_cache.as_ref().unwrap())?) Envelopes::try_from(self._raw_msgs_cache.as_ref().unwrap())
} }
/// Find a message by sequence number. /// Find a message by sequence number.
@ -201,9 +200,9 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
.context(r#"cannot fetch messages "{}""#)?; .context(r#"cannot fetch messages "{}""#)?;
let fetch = fetches let fetch = fetches
.first() .first()
.ok_or(anyhow!(r#"cannot find message "{}"#, seq))?; .ok_or_else(|| anyhow!(r#"cannot find message "{}"#, seq))?;
Ok(Msg::try_from(fetch)?) Msg::try_from(fetch)
} }
fn find_raw_msg(&mut self, seq: &str) -> Result<Vec<u8>> { fn find_raw_msg(&mut self, seq: &str) -> Result<Vec<u8>> {
@ -217,14 +216,14 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
.context(r#"cannot fetch raw messages "{}""#)?; .context(r#"cannot fetch raw messages "{}""#)?;
let fetch = fetches let fetch = fetches
.first() .first()
.ok_or(anyhow!(r#"cannot find raw message "{}"#, seq))?; .ok_or_else(|| anyhow!(r#"cannot find raw message "{}"#, seq))?;
Ok(fetch.body().map(Vec::from).unwrap_or_default()) Ok(fetch.body().map(Vec::from).unwrap_or_default())
} }
fn append_raw_msg_with_flags(&mut self, mbox: &Mbox, msg: &[u8], flags: Flags) -> Result<()> { fn append_raw_msg_with_flags(&mut self, mbox: &Mbox, msg: &[u8], flags: Flags) -> Result<()> {
self.sess()? self.sess()?
.append(&mbox.name, &msg) .append(&mbox.name, msg)
.flags(flags.0) .flags(flags.0)
.finish() .finish()
.context(format!(r#"cannot append message to "{}""#, mbox.name))?; .context(format!(r#"cannot append message to "{}""#, mbox.name))?;
@ -250,8 +249,11 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
.context(format!("cannot examine mailbox `{}`", &self.mbox.name))?; .context(format!("cannot examine mailbox `{}`", &self.mbox.name))?;
debug!("init messages hashset"); debug!("init messages hashset");
let mut msgs_set: HashSet<u32> = let mut msgs_set: HashSet<u32> = self
HashSet::from_iter(self.search_new_msgs()?.iter().cloned()); .search_new_msgs()?
.iter()
.cloned()
.collect::<HashSet<_>>();
trace!("messages hashset: {:?}", msgs_set); trace!("messages hashset: {:?}", msgs_set);
loop { loop {
@ -271,7 +273,7 @@ impl<'a> ImapServiceInterface<'a> for ImapService<'a> {
let uids: Vec<u32> = self let uids: Vec<u32> = self
.search_new_msgs()? .search_new_msgs()?
.into_iter() .into_iter()
.filter(|uid| msgs_set.get(&uid).is_none()) .filter(|uid| -> bool { msgs_set.get(uid).is_none() })
.collect(); .collect();
debug!("found {} new messages not in hashset", uids.len()); debug!("found {} new messages not in hashset", uids.len());
trace!("messages hashet: {:?}", msgs_set); trace!("messages hashet: {:?}", msgs_set);

View file

@ -32,7 +32,7 @@ impl<'a> Deref for Mboxes<'a> {
impl<'a> PrintTable for Mboxes<'a> { impl<'a> PrintTable for Mboxes<'a> {
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> { fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
writeln!(writter)?; writeln!(writter)?;
Table::print(writter, &self, opts)?; Table::print(writter, self, opts)?;
writeln!(writter)?; writeln!(writter)?;
Ok(()) Ok(())
} }

View file

@ -39,7 +39,7 @@ impl<'a> TryFrom<&'a RawEnvelope> for Envelope<'a> {
fn try_from(fetch: &'a RawEnvelope) -> Result<Envelope> { fn try_from(fetch: &'a RawEnvelope) -> Result<Envelope> {
let envelope = fetch let envelope = fetch
.envelope() .envelope()
.ok_or(anyhow!("cannot get envelope of message {}", fetch.message))?; .ok_or_else(|| anyhow!("cannot get envelope of message {}", fetch.message))?;
// Get the sequence number // Get the sequence number
let id = fetch.message; let id = fetch.message;
@ -57,7 +57,7 @@ impl<'a> TryFrom<&'a RawEnvelope> for Envelope<'a> {
fetch.message fetch.message
)) ))
}) })
.unwrap_or(Ok(String::default()))? .unwrap_or_else(|| Ok(String::default()))?
.into(); .into();
// Get the sender // Get the sender
@ -66,7 +66,7 @@ impl<'a> TryFrom<&'a RawEnvelope> for Envelope<'a> {
.as_ref() .as_ref()
.and_then(|addrs| addrs.get(0)) .and_then(|addrs| addrs.get(0))
.or_else(|| envelope.from.as_ref().and_then(|addrs| addrs.get(0))) .or_else(|| envelope.from.as_ref().and_then(|addrs| addrs.get(0)))
.ok_or(anyhow!("cannot get sender of message {}", fetch.message))?; .ok_or_else(|| anyhow!("cannot get sender of message {}", fetch.message))?;
let sender = if let Some(ref name) = sender.name { let sender = if let Some(ref name) = sender.name {
rfc2047_decoder::decode(&name.to_vec()).context(format!( rfc2047_decoder::decode(&name.to_vec()).context(format!(
"cannot decode sender's name of message {}", "cannot decode sender's name of message {}",
@ -76,10 +76,7 @@ impl<'a> TryFrom<&'a RawEnvelope> for Envelope<'a> {
let mbox = sender let mbox = sender
.mailbox .mailbox
.as_ref() .as_ref()
.ok_or(anyhow!( .ok_or_else(|| anyhow!("cannot get sender's mailbox of message {}", fetch.message))
"cannot get sender's mailbox of message {}",
fetch.message
))
.and_then(|mbox| { .and_then(|mbox| {
rfc2047_decoder::decode(&mbox.to_vec()).context(format!( rfc2047_decoder::decode(&mbox.to_vec()).context(format!(
"cannot decode sender's mailbox of message {}", "cannot decode sender's mailbox of message {}",
@ -89,10 +86,7 @@ impl<'a> TryFrom<&'a RawEnvelope> for Envelope<'a> {
let host = sender let host = sender
.host .host
.as_ref() .as_ref()
.ok_or(anyhow!( .ok_or_else(|| anyhow!("cannot get sender's host of message {}", fetch.message))
"cannot get sender's host of message {}",
fetch.message
))
.and_then(|host| { .and_then(|host| {
rfc2047_decoder::decode(&host.to_vec()).context(format!( rfc2047_decoder::decode(&host.to_vec()).context(format!(
"cannot decode sender's host of message {}", "cannot decode sender's host of message {}",
@ -133,11 +127,7 @@ impl<'a> Table for Envelope<'a> {
let unseen = !self.flags.contains(&Flag::Seen); let unseen = !self.flags.contains(&Flag::Seen);
let subject = &self.subject; let subject = &self.subject;
let sender = &self.sender; let sender = &self.sender;
let date = self let date = self.date.as_deref().unwrap_or_default();
.date
.as_ref()
.map(|date| date.as_str())
.unwrap_or_default();
Row::new() Row::new()
.cell(Cell::new(id).bold_if(unseen).red()) .cell(Cell::new(id).bold_if(unseen).red())
.cell(Cell::new(flags).bold_if(unseen).white()) .cell(Cell::new(flags).bold_if(unseen).white())

View file

@ -39,7 +39,7 @@ impl<'a> TryFrom<&'a RawEnvelopes> for Envelopes<'a> {
impl<'a> PrintTable for Envelopes<'a> { impl<'a> PrintTable for Envelopes<'a> {
fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> { fn print_table(&self, writter: &mut dyn WriteColor, opts: PrintTableOpts) -> Result<()> {
writeln!(writter)?; writeln!(writter)?;
Table::print(writter, &self, opts)?; Table::print(writter, self, opts)?;
writeln!(writter)?; writeln!(writter)?;
Ok(()) Ok(())
} }

View file

@ -126,16 +126,14 @@ impl<'a> From<Vec<&'a str>> for Flags {
let mut map: HashSet<Flag<'static>> = HashSet::new(); let mut map: HashSet<Flag<'static>> = HashSet::new();
for f in flags { for f in flags {
match f { match f.to_lowercase().as_str() {
"Answered" | _ if f.eq_ignore_ascii_case("answered") => map.insert(Flag::Answered), "answered" => map.insert(Flag::Answered),
"Deleted" | _ if f.eq_ignore_ascii_case("deleted") => map.insert(Flag::Deleted), "deleted" => map.insert(Flag::Deleted),
"Draft" | _ if f.eq_ignore_ascii_case("draft") => map.insert(Flag::Draft), "draft" => map.insert(Flag::Draft),
"Flagged" | _ if f.eq_ignore_ascii_case("flagged") => map.insert(Flag::Flagged), "flagged" => map.insert(Flag::Flagged),
"MayCreate" | _ if f.eq_ignore_ascii_case("maycreate") => { "maycreate" => map.insert(Flag::MayCreate),
map.insert(Flag::MayCreate) "recent" => map.insert(Flag::Recent),
} "seen" => map.insert(Flag::Seen),
"Recent" | _ if f.eq_ignore_ascii_case("recent") => map.insert(Flag::Recent),
"Seen" | _ if f.eq_ignore_ascii_case("seen") => map.insert(Flag::Seen),
custom => map.insert(Flag::Custom(Cow::Owned(custom.into()))), custom => map.insert(Flag::Custom(Cow::Owned(custom.into()))),
}; };
} }

View file

@ -200,11 +200,11 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
} }
if let Some(m) = m.subcommand_matches("template") { if let Some(m) = m.subcommand_matches("template") {
return Ok(Some(Command::Tpl(tpl_arg::matches(&m)?))); return Ok(Some(Command::Tpl(tpl_arg::matches(m)?)));
} }
if let Some(m) = m.subcommand_matches("flag") { if let Some(m) = m.subcommand_matches("flag") {
return Ok(Some(Command::Flag(flag_arg::matches(&m)?))); return Ok(Some(Command::Flag(flag_arg::matches(m)?)));
} }
debug!("default list command matched"); debug!("default list command matched");

View file

@ -194,14 +194,18 @@ impl Msg {
.date .date
.as_ref() .as_ref()
.map(|date| date.format("%d %b %Y, at %H:%M").to_string()) .map(|date| date.format("%d %b %Y, at %H:%M").to_string())
.unwrap_or("unknown date".into()); .unwrap_or_else(|| "unknown date".into());
let sender = self let sender = self
.reply_to .reply_to
.as_ref() .as_ref()
.or(self.from.as_ref()) .or_else(|| self.from.as_ref())
.and_then(|addrs| addrs.first()) .and_then(|addrs| addrs.first())
.map(|addr| addr.name.to_owned().unwrap_or(addr.email.to_string())) .map(|addr| {
.unwrap_or("unknown sender".into()); addr.name
.to_owned()
.unwrap_or_else(|| addr.email.to_string())
})
.unwrap_or_else(|| "unknown sender".into());
let mut content = format!("\n\nOn {}, {} wrote:\n", date, sender); let mut content = format!("\n\nOn {}, {} wrote:\n", date, sender);
let mut glue = ""; let mut glue = "";
@ -210,8 +214,8 @@ impl Msg {
break; break;
} }
content.push_str(glue); content.push_str(glue);
content.push_str(">"); content.push('>');
content.push_str(if line.starts_with(">") { "" } else { " " }); content.push_str(if line.starts_with('>') { "" } else { " " });
content.push_str(line); content.push_str(line);
glue = "\n"; glue = "\n";
} }
@ -239,7 +243,7 @@ impl Msg {
self.in_reply_to = None; self.in_reply_to = None;
// From // From
self.from = Some(vec![account_addr.to_owned()]); self.from = Some(vec![account_addr]);
// To // To
self.to = Some(vec![]); self.to = Some(vec![]);
@ -270,7 +274,7 @@ impl Msg {
content.push_str(&addr.to_string()); content.push_str(&addr.to_string());
glue = ", "; glue = ", ";
} }
content.push_str("\n"); content.push('\n');
} }
if let Some(addrs) = prev_to.as_ref() { if let Some(addrs) = prev_to.as_ref() {
content.push_str("To: "); content.push_str("To: ");
@ -280,9 +284,9 @@ impl Msg {
content.push_str(&addr.to_string()); content.push_str(&addr.to_string());
glue = ", "; glue = ", ";
} }
content.push_str("\n"); content.push('\n');
} }
content.push_str("\n"); content.push('\n');
content.push_str(&self.fold_text_parts("plain")); content.push_str(&self.fold_text_parts("plain"));
self.parts self.parts
.replace_text_plain_parts_with(TextPlainPart { content }); .replace_text_plain_parts_with(TextPlainPart { content });
@ -386,7 +390,7 @@ impl Msg {
let path = PathBuf::from(path.to_string()); let path = PathBuf::from(path.to_string());
let filename: String = path let filename: String = path
.file_name() .file_name()
.ok_or(anyhow!("cannot get file name of attachment {:?}", path))? .ok_or_else(|| anyhow!("cannot get file name of attachment {:?}", path))?
.to_string_lossy() .to_string_lossy()
.into(); .into();
let content = fs::read(&path).context(format!("cannot read attachment {:?}", path))?; let content = fs::read(&path).context(format!("cannot read attachment {:?}", path))?;
@ -427,17 +431,11 @@ impl Msg {
match part { match part {
Part::Binary(_) => self.parts.push(part), Part::Binary(_) => self.parts.push(part),
Part::TextPlain(_) => { Part::TextPlain(_) => {
self.parts.retain(|p| match p { self.parts.retain(|p| !matches!(p, Part::TextPlain(_)));
Part::TextPlain(_) => false,
_ => true,
});
self.parts.push(part); self.parts.push(part);
} }
Part::TextHtml(_) => { Part::TextHtml(_) => {
self.parts.retain(|p| match p { self.parts.retain(|p| !matches!(p, Part::TextHtml(_)));
Part::TextHtml(_) => false,
_ => true,
});
self.parts.push(part); self.parts.push(part);
} }
} }
@ -507,7 +505,7 @@ impl Msg {
)); ));
// Headers <=> body separator // Headers <=> body separator
tpl.push_str("\n"); tpl.push('\n');
// Body // Body
if let Some(body) = opts.body { if let Some(body) = opts.body {
@ -525,7 +523,7 @@ impl Msg {
tpl.push_str(sig); tpl.push_str(sig);
} }
tpl.push_str("\n"); tpl.push('\n');
trace!("template: {:#?}", tpl); trace!("template: {:#?}", tpl);
tpl tpl
@ -542,49 +540,45 @@ impl Msg {
let val = String::from_utf8(header.get_value_raw().to_vec()) let val = String::from_utf8(header.get_value_raw().to_vec())
.map(|val| val.trim().to_string())?; .map(|val| val.trim().to_string())?;
match key.as_str() { match key.to_lowercase().as_str() {
"Message-Id" | _ if key.eq_ignore_ascii_case("message-id") => { "message-id" => msg.message_id = Some(val.to_owned()),
msg.message_id = Some(val.to_owned()) "from" => {
}
"From" | _ if key.eq_ignore_ascii_case("from") => {
msg.from = Some( msg.from = Some(
val.split(',') val.split(',')
.filter_map(|addr| addr.parse().ok()) .filter_map(|addr| addr.parse().ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }
"To" | _ if key.eq_ignore_ascii_case("to") => { "to" => {
msg.to = Some( msg.to = Some(
val.split(',') val.split(',')
.filter_map(|addr| addr.parse().ok()) .filter_map(|addr| addr.parse().ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }
"Reply-To" | _ if key.eq_ignore_ascii_case("reply-to") => { "reply-to" => {
msg.reply_to = Some( msg.reply_to = Some(
val.split(',') val.split(',')
.filter_map(|addr| addr.parse().ok()) .filter_map(|addr| addr.parse().ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }
"In-Reply-To" | _ if key.eq_ignore_ascii_case("in-reply-to") => { "in-reply-to" => msg.in_reply_to = Some(val.to_owned()),
msg.in_reply_to = Some(val.to_owned()) "cc" => {
}
"Cc" | _ if key.eq_ignore_ascii_case("cc") => {
msg.cc = Some( msg.cc = Some(
val.split(',') val.split(',')
.filter_map(|addr| addr.parse().ok()) .filter_map(|addr| addr.parse().ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }
"Bcc" | _ if key.eq_ignore_ascii_case("bcc") => { "bcc" => {
msg.bcc = Some( msg.bcc = Some(
val.split(',') val.split(',')
.filter_map(|addr| addr.parse().ok()) .filter_map(|addr| addr.parse().ok())
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
} }
"Subject" | _ if key.eq_ignore_ascii_case("subject") => { "subject" => {
msg.subject = val; msg.subject = val;
} }
_ => (), _ => (),
@ -696,7 +690,7 @@ impl<'a> TryFrom<&'a imap::types::Fetch> for Msg {
fn try_from(fetch: &'a imap::types::Fetch) -> Result<Msg> { fn try_from(fetch: &'a imap::types::Fetch) -> Result<Msg> {
let envelope = fetch let envelope = fetch
.envelope() .envelope()
.ok_or(anyhow!("cannot get envelope of message {}", fetch.message))?; .ok_or_else(|| anyhow!("cannot get envelope of message {}", fetch.message))?;
// Get the sequence number // Get the sequence number
let id = fetch.message; let id = fetch.message;
@ -714,13 +708,13 @@ impl<'a> TryFrom<&'a imap::types::Fetch> for Msg {
fetch.message fetch.message
)) ))
}) })
.unwrap_or(Ok(String::default()))?; .unwrap_or_else(|| Ok(String::default()))?;
// Get the sender(s) address(es) // Get the sender(s) address(es)
let from = match envelope let from = match envelope
.sender .sender
.as_ref() .as_deref()
.or_else(|| envelope.from.as_ref()) .or_else(|| envelope.from.as_deref())
.map(parse_addrs) .map(parse_addrs)
{ {
Some(addrs) => Some(addrs?), Some(addrs) => Some(addrs?),
@ -773,7 +767,7 @@ impl<'a> TryFrom<&'a imap::types::Fetch> for Msg {
&mailparse::parse_mail( &mailparse::parse_mail(
fetch fetch
.body() .body()
.ok_or(anyhow!("cannot get body of message {}", id))?, .ok_or_else(|| anyhow!("cannot get body of message {}", id))?,
) )
.context(format!("cannot parse body of message {}", id))?, .context(format!("cannot parse body of message {}", id))?,
); );
@ -782,13 +776,13 @@ impl<'a> TryFrom<&'a imap::types::Fetch> for Msg {
id, id,
flags, flags,
subject, subject,
message_id,
from, from,
reply_to, reply_to,
in_reply_to,
to, to,
cc, cc,
bcc, bcc,
in_reply_to,
message_id,
date, date,
parts, parts,
}) })
@ -802,20 +796,20 @@ pub fn parse_addr(addr: &imap_proto::Address) -> Result<Addr> {
.map(|name| { .map(|name| {
rfc2047_decoder::decode(&name.to_vec()) rfc2047_decoder::decode(&name.to_vec())
.context("cannot decode address name") .context("cannot decode address name")
.map(|name| Some(name)) .map(Some)
}) })
.unwrap_or(Ok(None))?; .unwrap_or(Ok(None))?;
let mbox = addr let mbox = addr
.mailbox .mailbox
.as_ref() .as_ref()
.ok_or(anyhow!("cannot get address mailbox")) .ok_or_else(|| anyhow!("cannot get address mailbox"))
.and_then(|mbox| { .and_then(|mbox| {
rfc2047_decoder::decode(&mbox.to_vec()).context("cannot decode address mailbox") rfc2047_decoder::decode(&mbox.to_vec()).context("cannot decode address mailbox")
})?; })?;
let host = addr let host = addr
.host .host
.as_ref() .as_ref()
.ok_or(anyhow!("cannot get address host")) .ok_or_else(|| anyhow!("cannot get address host"))
.and_then(|host| { .and_then(|host| {
rfc2047_decoder::decode(&host.to_vec()).context("cannot decode address host") rfc2047_decoder::decode(&host.to_vec()).context("cannot decode address host")
})?; })?;
@ -823,7 +817,7 @@ pub fn parse_addr(addr: &imap_proto::Address) -> Result<Addr> {
Ok(Addr::new(name, lettre::Address::new(mbox, host)?)) Ok(Addr::new(name, lettre::Address::new(mbox, host)?))
} }
pub fn parse_addrs(addrs: &Vec<imap_proto::Address>) -> Result<Vec<Addr>> { pub fn parse_addrs(addrs: &[imap_proto::Address]) -> Result<Vec<Addr>> {
let mut parsed_addrs = vec![]; let mut parsed_addrs = vec![];
for addr in addrs { for addr in addrs {
parsed_addrs parsed_addrs
@ -833,7 +827,7 @@ pub fn parse_addrs(addrs: &Vec<imap_proto::Address>) -> Result<Vec<Addr>> {
} }
pub fn parse_some_addrs(addrs: &Option<Vec<imap_proto::Address>>) -> Result<Option<Vec<Addr>>> { pub fn parse_some_addrs(addrs: &Option<Vec<imap_proto::Address>>) -> Result<Option<Vec<Addr>>> {
Ok(match addrs.as_ref().map(parse_addrs) { Ok(match addrs.as_deref().map(parse_addrs) {
Some(addrs) => Some(addrs?), Some(addrs) => Some(addrs?),
None => None, None => None,
}) })

View file

@ -21,6 +21,7 @@ use crate::{
mbox::Mbox, mbox::Mbox,
msg::{Flags, Msg, Part, TextPlainPart}, msg::{Flags, Msg, Part, TextPlainPart},
smtp::SmtpServiceInterface, smtp::SmtpServiceInterface,
Parts,
}, },
output::{PrintTableOpts, PrinterService}, output::{PrintTableOpts, PrinterService},
}; };
@ -32,7 +33,7 @@ pub fn attachments<'a, Printer: PrinterService, ImapService: ImapServiceInterfac
printer: &mut Printer, printer: &mut Printer,
imap: &mut ImapService, imap: &mut ImapService,
) -> Result<()> { ) -> Result<()> {
let attachments = imap.find_msg(&seq)?.attachments(); let attachments = imap.find_msg(seq)?.attachments();
let attachments_len = attachments.len(); let attachments_len = attachments.len();
debug!( debug!(
r#"{} attachment(s) found for message "{}""#, r#"{} attachment(s) found for message "{}""#,
@ -60,7 +61,7 @@ pub fn copy<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
imap: &mut ImapService, imap: &mut ImapService,
) -> Result<()> { ) -> Result<()> {
let mbox = Mbox::new(mbox); let mbox = Mbox::new(mbox);
let msg = imap.find_raw_msg(&seq)?; let msg = imap.find_raw_msg(seq)?;
let flags = Flags::try_from(vec![Flag::Seen])?; let flags = Flags::try_from(vec![Flag::Seen])?;
imap.append_raw_msg_with_flags(&mbox, &msg, flags)?; imap.append_raw_msg_with_flags(&mbox, &msg, flags)?;
printer.print(format!( printer.print(format!(
@ -135,7 +136,7 @@ pub fn mailto<
) -> Result<()> { ) -> Result<()> {
let to: Vec<lettre::message::Mailbox> = url let to: Vec<lettre::message::Mailbox> = url
.path() .path()
.split(";") .split(';')
.filter_map(|s| s.parse().ok()) .filter_map(|s| s.parse().ok())
.collect(); .collect();
let mut cc = Vec::new(); let mut cc = Vec::new();
@ -161,16 +162,18 @@ pub fn mailto<
} }
} }
let mut msg = Msg::default(); let msg = Msg {
from: Some(vec![account.address().parse()?]),
to: if to.is_empty() { None } else { Some(to) },
cc: if cc.is_empty() { None } else { Some(cc) },
bcc: if bcc.is_empty() { None } else { Some(bcc) },
subject: subject.into(),
parts: Parts(vec![Part::TextPlain(TextPlainPart {
content: body.into(),
})]),
..Msg::default()
};
msg.from = Some(vec![account.address().parse()?]);
msg.to = if to.is_empty() { None } else { Some(to) };
msg.cc = if cc.is_empty() { None } else { Some(cc) };
msg.bcc = if bcc.is_empty() { None } else { Some(bcc) };
msg.subject = subject.into();
msg.parts.push(Part::TextPlain(TextPlainPart {
content: body.into(),
}));
msg.edit_with_editor(account, printer, imap, smtp) msg.edit_with_editor(account, printer, imap, smtp)
} }
@ -185,7 +188,7 @@ pub fn move_<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>
) -> Result<()> { ) -> Result<()> {
// Copy the message to targetted mailbox // Copy the message to targetted mailbox
let mbox = Mbox::new(mbox); let mbox = Mbox::new(mbox);
let msg = imap.find_raw_msg(&seq)?; let msg = imap.find_raw_msg(seq)?;
let flags = Flags::try_from(vec![Flag::Seen])?; let flags = Flags::try_from(vec![Flag::Seen])?;
imap.append_raw_msg_with_flags(&mbox, &msg, flags)?; imap.append_raw_msg_with_flags(&mbox, &msg, flags)?;
@ -210,9 +213,9 @@ pub fn read<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
) -> Result<()> { ) -> Result<()> {
let msg = if raw { let msg = if raw {
// Emails don't always have valid utf8. Using "lossy" to display what we can. // Emails don't always have valid utf8. Using "lossy" to display what we can.
String::from_utf8_lossy(&imap.find_raw_msg(&seq)?).into_owned() String::from_utf8_lossy(&imap.find_raw_msg(seq)?).into_owned()
} else { } else {
imap.find_msg(&seq)?.fold_text_parts(text_mime) imap.find_msg(seq)?.fold_text_parts(text_mime)
}; };
printer.print(msg) printer.print(msg)
@ -254,8 +257,7 @@ pub fn save<'a, Printer: PrinterService, ImapService: ImapServiceInterface<'a>>(
io::stdin() io::stdin()
.lock() .lock()
.lines() .lines()
.filter_map(|ln| ln.ok()) .filter_map(Result::ok)
.map(|ln| ln.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\r\n") .join("\r\n")
}; };
@ -301,13 +303,12 @@ pub fn send<
io::stdin() io::stdin()
.lock() .lock()
.lines() .lines()
.filter_map(|ln| ln.ok()) .filter_map(Result::ok)
.map(|ln| ln.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\r\n") .join("\r\n")
}; };
let msg = Msg::from_tpl(&raw_msg.to_string())?; let msg = Msg::from_tpl(&raw_msg)?;
let envelope: lettre::address::Envelope = msg.try_into()?; let envelope: lettre::address::Envelope = msg.try_into()?;
smtp.send_raw_msg(&envelope, raw_msg.as_bytes())?; smtp.send_raw_msg(&envelope, raw_msg.as_bytes())?;
debug!("message sent!"); debug!("message sent!");

View file

@ -39,24 +39,12 @@ pub struct Parts(pub Vec<Part>);
impl Parts { impl Parts {
pub fn replace_text_plain_parts_with(&mut self, part: TextPlainPart) { pub fn replace_text_plain_parts_with(&mut self, part: TextPlainPart) {
self.retain(|part| { self.retain(|part| !matches!(part, Part::TextPlain(_)));
if let Part::TextPlain(_) = part {
false
} else {
true
}
});
self.push(Part::TextPlain(part)); self.push(Part::TextPlain(part));
} }
pub fn replace_text_html_parts_with(&mut self, part: TextHtmlPart) { pub fn replace_text_html_parts_with(&mut self, part: TextHtmlPart) {
self.retain(|part| { self.retain(|part| !matches!(part, Part::TextHtml(_)));
if let Part::TextHtml(_) = part {
false
} else {
true
}
});
self.push(Part::TextHtml(part)); self.push(Part::TextHtml(part));
} }
} }
@ -92,7 +80,7 @@ fn build_parts_map_rec(part: &mailparse::ParsedMail, parts: &mut Vec<Part>) {
.params .params
.get("filename") .get("filename")
.map(String::from) .map(String::from)
.unwrap_or(String::from("noname")); .unwrap_or_else(|| String::from("noname"));
let content = part.get_body_raw().unwrap_or_default(); let content = part.get_body_raw().unwrap_or_default();
let mime = tree_magic::from_u8(&content); let mime = tree_magic::from_u8(&content);
parts.push(Part::Binary(BinaryPart { parts.push(Part::Binary(BinaryPart {
@ -103,16 +91,14 @@ fn build_parts_map_rec(part: &mailparse::ParsedMail, parts: &mut Vec<Part>) {
} }
// TODO: manage other use cases // TODO: manage other use cases
_ => { _ => {
part.get_headers() if let Some(ctype) = part.get_headers().get_first_value("content-type") {
.get_first_value("content-type") let content = part.get_body().unwrap_or_default();
.map(|ctype| { if ctype.starts_with("text/plain") {
let content = part.get_body().unwrap_or_default(); parts.push(Part::TextPlain(TextPlainPart { content }))
if ctype.starts_with("text/plain") { } else if ctype.starts_with("text/html") {
parts.push(Part::TextPlain(TextPlainPart { content })) parts.push(Part::TextHtml(TextHtmlPart { content }))
} else if ctype.starts_with("text/html") { }
parts.push(Part::TextHtml(TextHtmlPart { content })) };
}
});
} }
}; };
} else { } else {

View file

@ -1,6 +1,4 @@
use anyhow::Result; use anyhow::Result;
use clap;
use env_logger;
use output::StdoutPrinter; use output::StdoutPrinter;
use std::{convert::TryFrom, env}; use std::{convert::TryFrom, env};
use url::Url; use url::Url;
@ -36,6 +34,7 @@ fn create_app<'a>() -> clap::App<'a, 'a> {
.subcommands(msg_arg::subcmds()) .subcommands(msg_arg::subcmds())
} }
#[allow(clippy::single_match)]
fn main() -> Result<()> { fn main() -> Result<()> {
// Init env logger // Init env logger
env_logger::init_from_env( env_logger::init_from_env(

View file

@ -36,9 +36,9 @@ impl TryFrom<Option<&str>> for OutputFmt {
impl Display for OutputFmt { impl Display for OutputFmt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let fmt = match self { let fmt = match *self {
&OutputFmt::Json => "JSON", OutputFmt::Json => "JSON",
&OutputFmt::Plain => "Plain", OutputFmt::Plain => "Plain",
}; };
write!(f, "{}", fmt) write!(f, "{}", fmt)
} }

View file

@ -227,13 +227,11 @@ where
trace!("number of spaces added to shrinked value: {}", spaces_count); trace!("number of spaces added to shrinked value: {}", spaces_count);
value.push_str(&" ".repeat(spaces_count)); value.push_str(&" ".repeat(spaces_count));
cell.value = value; cell.value = value;
cell.print(writter)?;
} else { } else {
trace!("cell is not overflowing"); trace!("cell is not overflowing");
let spaces_count = cell_width - cell.unicode_width() + 1; let spaces_count = cell_width - cell.unicode_width() + 1;
trace!("number of spaces added to value: {}", spaces_count); trace!("number of spaces added to value: {}", spaces_count);
cell.value.push_str(&" ".repeat(spaces_count)); cell.value.push_str(&" ".repeat(spaces_count));
cell.print(writter)?;
} }
} else { } else {
trace!("table is not overflowing or cell is not shrinkable"); trace!("table is not overflowing or cell is not shrinkable");
@ -242,8 +240,8 @@ where
let spaces_count = cell_widths[i] - cell.unicode_width() + 1; let spaces_count = cell_widths[i] - cell.unicode_width() + 1;
trace!("number of spaces added to value: {}", spaces_count); trace!("number of spaces added to value: {}", spaces_count);
cell.value.push_str(&" ".repeat(spaces_count)); cell.value.push_str(&" ".repeat(spaces_count));
cell.print(writter)?;
} }
cell.print(writter)?;
glue = Cell::new("").ansi_256(8); glue = Cell::new("").ansi_256(8);
} }
writeln!(writter)?; writeln!(writter)?;