mirror of
https://github.com/soywod/himalaya.git
synced 2024-07-05 09:05:13 +00:00
review flag command, remove lib + dead code
This commit is contained in:
parent
71818e04b4
commit
65664d5405
|
@ -77,96 +77,6 @@ impl Config {
|
|||
Ok(path)
|
||||
}
|
||||
|
||||
/// Returns the account by the given name.
|
||||
/// If `name` is `None`, then the default account is returned.
|
||||
pub fn find_account_by_name(&self, name: Option<&str>) -> Result<&ConfigAccountEntry> {
|
||||
match name {
|
||||
Some("") | None => self
|
||||
.accounts
|
||||
.iter()
|
||||
.find(|(_, account)| account.default.unwrap_or(false))
|
||||
.map(|(_, account)| account)
|
||||
.ok_or_else(|| anyhow!("cannot find default account")),
|
||||
Some(name) => self
|
||||
.accounts
|
||||
.get(name)
|
||||
.ok_or_else(|| anyhow!("cannot find account `{}`", name)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the given filename in the download directory.
|
||||
/// You can imagine this as:
|
||||
/// ```skip
|
||||
/// Account-specifique-download-dir-path + Attachment-Filename
|
||||
/// ```
|
||||
pub fn downloads_filepath(&self, account: &ConfigAccountEntry, filename: &str) -> PathBuf {
|
||||
account
|
||||
.downloads_dir
|
||||
.as_ref()
|
||||
.and_then(|dir| dir.to_str())
|
||||
.and_then(|dir| shellexpand::full(dir).ok())
|
||||
.map(|dir| PathBuf::from(dir.to_string()))
|
||||
.unwrap_or(
|
||||
self.downloads_dir
|
||||
.as_ref()
|
||||
.and_then(|dir| dir.to_str())
|
||||
.and_then(|dir| shellexpand::full(dir).ok())
|
||||
.map(|dir| PathBuf::from(dir.to_string()))
|
||||
.unwrap_or(env::temp_dir()),
|
||||
)
|
||||
.join(filename)
|
||||
}
|
||||
|
||||
/// This is a little helper-function like which uses the the name and email
|
||||
/// of the account to create a valid address for the header of the headers
|
||||
/// of a msg.
|
||||
///
|
||||
/// # Hint
|
||||
/// If the name includes some special characters like a whitespace, comma or semicolon, then
|
||||
/// the name will be automatically wrapped between two `"`.
|
||||
///
|
||||
/// # Exapmle
|
||||
/// ```
|
||||
/// use himalaya::config::model::{Account, Config};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let config = Config::default();
|
||||
///
|
||||
/// let normal_account = Account::new(Some("Acc1"), "acc1@mail.com");
|
||||
/// // notice the semicolon in the name!
|
||||
/// let special_account = Account::new(Some("TL;DR"), "acc2@mail.com");
|
||||
///
|
||||
/// // -- Expeced outputs --
|
||||
/// let expected_normal = Account {
|
||||
/// name: Some("Acc1".to_string()),
|
||||
/// email: "acc1@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected_special = Account {
|
||||
/// name: Some("\"TL;DR\"".to_string()),
|
||||
/// email: "acc2@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(config.address(&normal_account), "Acc1 <acc1@mail.com>");
|
||||
/// assert_eq!(config.address(&special_account), "\"TL;DR\" <acc2@mail.com>");
|
||||
/// }
|
||||
/// ```
|
||||
pub fn address(&self, account: &ConfigAccountEntry) -> String {
|
||||
let name = account.name.as_ref().unwrap_or(&self.name);
|
||||
let has_special_chars = "()<>[]:;@.,".contains(|special_char| name.contains(special_char));
|
||||
|
||||
if name.is_empty() {
|
||||
format!("{}", account.email)
|
||||
} else if has_special_chars {
|
||||
// so the name has special characters => Wrap it with '"'
|
||||
format!("\"{}\" <{}>", name, account.email)
|
||||
} else {
|
||||
format!("{} <{}>", name, account.email)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_notify_cmd<S: AsRef<str>>(&self, subject: S, sender: S) -> Result<()> {
|
||||
let subject = subject.as_ref();
|
||||
let sender = sender.as_ref();
|
||||
|
@ -183,7 +93,7 @@ impl Config {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exec_watch_cmds(&self, account: &ConfigAccountEntry) -> Result<()> {
|
||||
pub fn _exec_watch_cmds(&self, account: &ConfigAccountEntry) -> Result<()> {
|
||||
let cmds = account
|
||||
.watch_cmds
|
||||
.as_ref()
|
||||
|
@ -323,28 +233,6 @@ impl Account {
|
|||
format!("{} <{}>", name, self.email)
|
||||
}
|
||||
}
|
||||
/// Returns the imap-host address + the port usage of the account
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
/// fn main () {
|
||||
/// let account = Account {
|
||||
/// imap_host: String::from("hostExample"),
|
||||
/// imap_port: 42,
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected_output = ("hostExample", 42);
|
||||
///
|
||||
/// assert_eq!(account.imap_addr(), expected_output);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn imap_addr(&self) -> (&str, u16) {
|
||||
debug!("host: {}", self.imap_host);
|
||||
debug!("port: {}", self.imap_port);
|
||||
(&self.imap_host, self.imap_port)
|
||||
}
|
||||
|
||||
/// Runs the given command in your password string and returns it.
|
||||
pub fn imap_passwd(&self) -> Result<String> {
|
||||
|
@ -364,83 +252,6 @@ impl Account {
|
|||
|
||||
Ok(SmtpCredentials::new(self.smtp_login.to_owned(), passwd))
|
||||
}
|
||||
|
||||
/// Creates a new account with the given values and returns it. All other attributes of the
|
||||
/// account are gonna be empty/None.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let account1 = Account::new(Some("Name1"), "email@address.com");
|
||||
/// let account2 = Account::new(None, "email@address.com");
|
||||
///
|
||||
/// let expected1 = Account {
|
||||
/// name: Some("Name1".to_string()),
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let expected2 = Account {
|
||||
/// email: "email@address.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account1, expected1);
|
||||
/// assert_eq!(account2, expected2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new<S: ToString + Default>(name: Option<S>, email_addr: S) -> Self {
|
||||
Self {
|
||||
name: name.unwrap_or_default().to_string(),
|
||||
email: email_addr.to_string(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new account with a custom signature. Passing `None` to `signature` sets the
|
||||
/// signature to `Account Signature`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust
|
||||
/// use himalaya::config::model::Account;
|
||||
///
|
||||
/// fn main() {
|
||||
///
|
||||
/// // the testing accounts
|
||||
/// let account_with_custom_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", Some("Custom signature! :)"));
|
||||
/// let account_with_default_signature = Account::new_with_signature(
|
||||
/// Some("Email name"), "some@mail.com", None);
|
||||
///
|
||||
/// // How they should look like
|
||||
/// let account_cmp1 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// signature: Some("Custom signature! :)".to_string()),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// let account_cmp2 = Account {
|
||||
/// name: Some("Email name".to_string()),
|
||||
/// email: "some@mail.com".to_string(),
|
||||
/// .. Account::default()
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(account_with_custom_signature, account_cmp1);
|
||||
/// assert_eq!(account_with_default_signature, account_cmp2);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_with_signature<S: AsRef<str> + ToString + Default>(
|
||||
name: Option<S>,
|
||||
email_addr: S,
|
||||
signature: Option<S>,
|
||||
) -> Self {
|
||||
let mut account = Account::new(name, email_addr);
|
||||
account.signature = signature.unwrap_or_default().to_string();
|
||||
account
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<(&'a Config, Option<&str>)> for Account {
|
||||
|
|
|
@ -33,9 +33,15 @@ pub trait ImapServiceInterface {
|
|||
) -> Result<Option<ImapMsgs>>;
|
||||
fn get_msg(&mut self, uid: &str) -> Result<Msg>;
|
||||
fn append_msg(&mut self, mbox: &Mbox, msg: &mut Msg) -> Result<()>;
|
||||
fn add_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()>;
|
||||
fn set_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()>;
|
||||
fn remove_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()>;
|
||||
/// Add flags to the given message UID sequence.
|
||||
///
|
||||
/// ```ignore
|
||||
/// let flags = Flags::from(vec![Flag::Seen, Flag::Deleted]);
|
||||
/// add_flags("5:10", flags)
|
||||
/// ```
|
||||
fn add_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()>;
|
||||
fn set_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()>;
|
||||
fn remove_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()>;
|
||||
fn expunge(&mut self) -> Result<()>;
|
||||
fn logout(&mut self) -> Result<()>;
|
||||
}
|
||||
|
@ -204,14 +210,7 @@ impl<'a> ImapServiceInterface for ImapService<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Add flags to the given message UID sequence.
|
||||
///
|
||||
/// ```ignore
|
||||
///
|
||||
/// let flags = Flags::from(vec![Flag::Seen, Flag::Deleted]);
|
||||
/// imap.add_flags("5:10", flags)
|
||||
/// ```
|
||||
fn add_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()> {
|
||||
fn add_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()> {
|
||||
let mbox = self.mbox.to_owned();
|
||||
let flags: String = flags.to_string();
|
||||
self.sess()?
|
||||
|
@ -244,9 +243,8 @@ impl<'a> ImapServiceInterface for ImapService<'a> {
|
|||
/// imap_conn.logout();
|
||||
/// }
|
||||
/// ```
|
||||
fn set_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()> {
|
||||
fn set_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()> {
|
||||
let mbox = self.mbox.to_owned();
|
||||
let flags: String = flags.to_string();
|
||||
self.sess()?
|
||||
.select(&mbox.name)
|
||||
.context(format!("cannot select mailbox `{}`", self.mbox.name))?;
|
||||
|
@ -258,7 +256,7 @@ impl<'a> ImapServiceInterface for ImapService<'a> {
|
|||
|
||||
/// Remove the flags to the message by the given information. Take a look on the example above.
|
||||
/// It's pretty similar.
|
||||
fn remove_flags(&mut self, uid_seq: &str, flags: Flags) -> Result<()> {
|
||||
fn remove_flags(&mut self, uid_seq: &str, flags: &Flags) -> Result<()> {
|
||||
let mbox = self.mbox.to_owned();
|
||||
let flags = flags.to_string();
|
||||
self.sess()?
|
||||
|
|
|
@ -101,15 +101,6 @@ pub struct Mbox {
|
|||
pub attributes: Attributes,
|
||||
}
|
||||
|
||||
impl Mbox {
|
||||
pub fn new<S: AsRef<str>>(name: S) -> Self {
|
||||
Self {
|
||||
name: name.as_ref().to_owned(),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mbox {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
|
@ -183,7 +183,7 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
|||
return Ok(Some(Command::Tpl(msg::tpl::arg::matches(&m)?)));
|
||||
}
|
||||
|
||||
if let Some(m) = m.subcommand_matches("flags") {
|
||||
if let Some(m) = m.subcommand_matches("flag") {
|
||||
return Ok(Some(Command::Flag(msg::flag::arg::matches(&m)?)));
|
||||
}
|
||||
|
||||
|
|
|
@ -20,31 +20,6 @@ pub struct Attachment {
|
|||
}
|
||||
|
||||
impl Attachment {
|
||||
/// Creates a new attachment.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use himalaya::msg::attachment::Attachment;
|
||||
/// let attachment = Attachment::new(
|
||||
/// "VIP Text",
|
||||
/// "text/plain",
|
||||
/// "Some very important text".as_bytes().to_vec());
|
||||
///
|
||||
/// ```
|
||||
pub fn new(filename: &str, content_type: &str, body_raw: Vec<u8>) -> Self {
|
||||
// Use the mime type `text/plain` per default
|
||||
let content_type: ContentType = match content_type.parse() {
|
||||
Ok(lettre_type) => lettre_type,
|
||||
Err(_) => ContentType::TEXT_PLAIN,
|
||||
};
|
||||
|
||||
Self {
|
||||
filename: filename.to_string(),
|
||||
content_type,
|
||||
body_raw,
|
||||
}
|
||||
}
|
||||
|
||||
/// This from function extracts one attachment of a parsed msg.
|
||||
/// If it couldn't create an attachment with the given parsed msg, than it will
|
||||
/// return `None`.
|
||||
|
|
|
@ -65,54 +65,6 @@ impl Body {
|
|||
html: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `Body` with `html` set.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::msg::body::Body;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let body = Body::new_with_html("Html body");
|
||||
///
|
||||
/// let expected_body = Body {
|
||||
/// text: None,
|
||||
/// html: Some("Html body".to_string()),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(body, expected_body);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_with_html<S: ToString>(html: S) -> Self {
|
||||
Self {
|
||||
plain: None,
|
||||
html: Some(html.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new isntance of `Body` with `text` and `html` set.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use himalaya::msg::body::Body;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let body = Body::new_with_both("Text body", "Html body");
|
||||
///
|
||||
/// let expected_body = Body {
|
||||
/// text: Some("Text body".to_string()),
|
||||
/// html: Some("Html body".to_string()),
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(body, expected_body);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn new_with_both<S: ToString>(text: S, html: S) -> Self {
|
||||
Self {
|
||||
plain: Some(text.to_string()),
|
||||
html: Some(html.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// == Traits ==
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
use anyhow::{anyhow, Context, Error, Result};
|
||||
use imap::types::{Fetch, Flag, ZeroCopy};
|
||||
use lettre::message::{
|
||||
header::ContentType, Attachment as lettre_Attachment, Mailbox, Message, MultiPart, SinglePart,
|
||||
};
|
||||
use log::debug;
|
||||
use mailparse;
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
convert::{From, TryFrom},
|
||||
fmt,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
config::entity::Account,
|
||||
|
@ -9,21 +17,10 @@ use crate::{
|
|||
attachment::entity::Attachment, body::entity::Body, flag::entity::Flags,
|
||||
header::entity::Headers,
|
||||
},
|
||||
ui::table::{Cell, Row, Table},
|
||||
};
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::ui::editor;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use lettre::message::{
|
||||
header::ContentType, Attachment as lettre_Attachment, Mailbox, Message, MultiPart, SinglePart,
|
||||
};
|
||||
|
||||
use std::{
|
||||
convert::{From, TryFrom},
|
||||
fmt,
|
||||
ui::{
|
||||
editor,
|
||||
table::{Cell, Row, Table},
|
||||
},
|
||||
};
|
||||
|
||||
/// Represents the msg in a serializeable form with additional values.
|
||||
|
@ -425,7 +422,6 @@ impl Msg {
|
|||
// We don't let this line compile, if we're doing
|
||||
// tests, because we just need to look, if the headers are set
|
||||
// correctly
|
||||
#[cfg(not(test))]
|
||||
let msg = editor::open_editor_with_tpl(msg.as_bytes())?;
|
||||
|
||||
// refresh the state of the msg
|
||||
|
@ -716,14 +712,6 @@ impl Msg {
|
|||
self.uid
|
||||
}
|
||||
|
||||
/// It returns the raw version of the Message. In general it's the structure
|
||||
/// how you get it if you get the data from the fetch. It's the output if
|
||||
/// you read a message with the `--raw` flag like this: `himalaya read
|
||||
/// --raw <UID>`.
|
||||
pub fn get_raw(&self) -> Vec<u8> {
|
||||
self.raw.clone()
|
||||
}
|
||||
|
||||
/// Returns the raw mail as a string instead of a Vector of bytes.
|
||||
pub fn get_raw_as_string(&self) -> Result<String> {
|
||||
let raw_message = String::from_utf8(self.raw.clone())
|
||||
|
|
|
@ -9,7 +9,7 @@ use log::debug;
|
|||
use crate::domain::msg;
|
||||
|
||||
type Uid<'a> = &'a str;
|
||||
type Flags<'a> = &'a str;
|
||||
type Flags<'a> = Vec<&'a str>;
|
||||
|
||||
/// Message flag commands.
|
||||
pub enum Command<'a> {
|
||||
|
@ -24,8 +24,8 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
|||
debug!("set command matched");
|
||||
let uid = m.value_of("uid").unwrap();
|
||||
debug!("uid: {}", uid);
|
||||
let flags = m.value_of("flags").unwrap();
|
||||
debug!("flags: {}", flags);
|
||||
let flags: Vec<&str> = m.values_of("flags").unwrap_or_default().collect();
|
||||
debug!("flags: `{:?}`", flags);
|
||||
return Ok(Some(Command::Set(uid, flags)));
|
||||
}
|
||||
|
||||
|
@ -33,8 +33,8 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
|||
debug!("add command matched");
|
||||
let uid = m.value_of("uid").unwrap();
|
||||
debug!("uid: {}", uid);
|
||||
let flags = m.value_of("flags").unwrap();
|
||||
debug!("flags: {}", flags);
|
||||
let flags: Vec<&str> = m.values_of("flags").unwrap_or_default().collect();
|
||||
debug!("flags: `{:?}`", flags);
|
||||
return Ok(Some(Command::Add(uid, flags)));
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
|||
debug!("remove command matched");
|
||||
let uid = m.value_of("uid").unwrap();
|
||||
debug!("uid: {}", uid);
|
||||
let flags = m.value_of("flags").unwrap();
|
||||
debug!("flags: {}", flags);
|
||||
let flags: Vec<&str> = m.values_of("flags").unwrap_or_default().collect();
|
||||
debug!("flags: `{:?}`", flags);
|
||||
return Ok(Some(Command::Remove(uid, flags)));
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,9 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Command<'a>>> {
|
|||
/// Message flag flags argument.
|
||||
fn flags_arg<'a>() -> Arg<'a, 'a> {
|
||||
Arg::with_name("flags")
|
||||
.help("IMAP flags (see https://tools.ietf.org/html/rfc3501#page-11). Just write the flag name without the backslash. Example: --flags \"Seen Answered\"")
|
||||
.help(
|
||||
"IMAP flags (they do not need to be prefixed with `\\` and they are case-insensitive)",
|
||||
)
|
||||
.value_name("FLAGS…")
|
||||
.multiple(true)
|
||||
.required(true)
|
||||
|
@ -61,7 +63,8 @@ fn flags_arg<'a>() -> Arg<'a, 'a> {
|
|||
|
||||
/// Message flag subcommands.
|
||||
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
||||
vec![SubCommand::with_name("flags")
|
||||
vec![SubCommand::with_name("flag")
|
||||
.aliases(&["flags", "flg"])
|
||||
.about("Handles flags")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
|
@ -72,7 +75,7 @@ pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
|
|||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("add")
|
||||
.about("Appends flags to a message")
|
||||
.about("Adds flags to a message")
|
||||
.arg(msg::arg::uid_arg())
|
||||
.arg(flags_arg()),
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ use serde::ser::{Serialize, SerializeSeq, Serializer};
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use std::convert::From;
|
||||
|
@ -70,27 +71,25 @@ impl Flags {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToString for Flags {
|
||||
fn to_string(&self) -> String {
|
||||
let mut flags = String::new();
|
||||
|
||||
impl fmt::Display for Flags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut glue = "";
|
||||
for flag in &self.0 {
|
||||
write!(f, "{}", glue)?;
|
||||
match flag {
|
||||
Flag::Seen => flags.push_str("\\Seen "),
|
||||
Flag::Answered => flags.push_str("\\Answered "),
|
||||
Flag::Flagged => flags.push_str("\\Flagged "),
|
||||
Flag::Deleted => flags.push_str("\\Deleted "),
|
||||
Flag::Draft => flags.push_str("\\Draft "),
|
||||
Flag::Recent => flags.push_str("\\Recent "),
|
||||
Flag::MayCreate => flags.push_str("\\MayCreate "),
|
||||
Flag::Custom(cow) => flags.push_str(&format!("\\{} ", cow)),
|
||||
_ => panic!("Unknown flag!"),
|
||||
Flag::Seen => write!(f, "\\Seen")?,
|
||||
Flag::Answered => write!(f, "\\Answered")?,
|
||||
Flag::Flagged => write!(f, "\\Flagged")?,
|
||||
Flag::Deleted => write!(f, "\\Deleted")?,
|
||||
Flag::Draft => write!(f, "\\Draft")?,
|
||||
Flag::Recent => write!(f, "\\Recent")?,
|
||||
Flag::MayCreate => write!(f, "\\MayCreate")?,
|
||||
Flag::Custom(cow) => write!(f, "{}", cow)?,
|
||||
_ => (),
|
||||
}
|
||||
glue = " ";
|
||||
}
|
||||
|
||||
// remove the trailing whitespaces
|
||||
flags = flags.trim_end_matches(' ').to_string();
|
||||
flags
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,13 +142,14 @@ impl From<&str> for Flags {
|
|||
|
||||
for flag in flags.split_ascii_whitespace() {
|
||||
match flag {
|
||||
"Seen" => content.insert(Flag::Seen),
|
||||
"Answered" => content.insert(Flag::Answered),
|
||||
"Deleted" => content.insert(Flag::Flagged),
|
||||
"Deleted" => content.insert(Flag::Deleted),
|
||||
"Draft" => content.insert(Flag::Draft),
|
||||
"Recent" => content.insert(Flag::Recent),
|
||||
"Flagged" => content.insert(Flag::Flagged),
|
||||
"MayCreate" => content.insert(Flag::MayCreate),
|
||||
_other => content.insert(Flag::Custom(Cow::Owned(_other.to_string()))),
|
||||
"Recent" => content.insert(Flag::Recent),
|
||||
"Seen" => content.insert(Flag::Seen),
|
||||
custom => content.insert(Flag::Custom(Cow::Owned(custom.to_string()))),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,29 @@ impl From<&str> for Flags {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Vec<&'a str>> for Flags {
|
||||
fn from(flags: Vec<&'a str>) -> Self {
|
||||
let mut map: HashSet<Flag<'static>> = HashSet::new();
|
||||
|
||||
for f in flags {
|
||||
match f {
|
||||
"Answered" | _ if f.eq_ignore_ascii_case("answered") => map.insert(Flag::Answered),
|
||||
"Deleted" | _ if f.eq_ignore_ascii_case("deleted") => map.insert(Flag::Deleted),
|
||||
"Draft" | _ if f.eq_ignore_ascii_case("draft") => map.insert(Flag::Draft),
|
||||
"Flagged" | _ if f.eq_ignore_ascii_case("flagged") => map.insert(Flag::Flagged),
|
||||
"MayCreate" | _ if f.eq_ignore_ascii_case("maycreate") => {
|
||||
map.insert(Flag::MayCreate)
|
||||
}
|
||||
"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()))),
|
||||
};
|
||||
}
|
||||
|
||||
Self(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Flags {
|
||||
type Target = HashSet<Flag<'static>>;
|
||||
|
||||
|
|
|
@ -1,36 +1,82 @@
|
|||
//! Module related to message flag handling.
|
||||
//!
|
||||
//! This module gathers all message flag commands.
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::domain::{imap::service::ImapServiceInterface, msg::flag::entity::Flags};
|
||||
use crate::{
|
||||
domain::{imap::service::ImapServiceInterface, msg::flag::entity::Flags},
|
||||
output::service::OutputServiceInterface,
|
||||
};
|
||||
|
||||
pub fn set<ImapService: ImapServiceInterface>(
|
||||
uid: &str,
|
||||
flags: &str,
|
||||
imap: &mut ImapService,
|
||||
/// Add flags from the given message UID sequence.
|
||||
/// Flags do not need to be prefixed with `\` and they are not case-sensitive.
|
||||
///
|
||||
/// ```ignore
|
||||
/// add("21", "\\Seen", &output, &mut imap)?;
|
||||
/// add("42", "recent", &output, &mut imap)?;
|
||||
/// add("1:10", "Answered custom", &output, &mut imap)?;
|
||||
/// ```
|
||||
pub fn add<'a, OutputService: OutputServiceInterface, ImapService: ImapServiceInterface>(
|
||||
uid: &'a str,
|
||||
flags: Vec<&'a str>,
|
||||
output: &'a OutputService,
|
||||
imap: &'a mut ImapService,
|
||||
) -> Result<()> {
|
||||
let flags = Flags::from(flags);
|
||||
imap.set_flags(uid, flags)?;
|
||||
imap.add_flags(uid, &flags)?;
|
||||
output.print(format!(
|
||||
r#"Flag(s) "{}" successfully added to message {}"#,
|
||||
flags, uid
|
||||
))?;
|
||||
imap.logout()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add<ImapService: ImapServiceInterface>(
|
||||
uid: &str,
|
||||
flags: &str,
|
||||
imap: &mut ImapService,
|
||||
/// Remove flags from the given message UID sequence.
|
||||
/// Flags do not need to be prefixed with `\` and they are not case-sensitive.
|
||||
///
|
||||
/// ```ignore
|
||||
/// remove("21", "\\Seen", &output, &mut imap)?;
|
||||
/// remove("42", "recent", &output, &mut imap)?;
|
||||
/// remove("1:10", "Answered custom", &output, &mut imap)?;
|
||||
/// ```
|
||||
pub fn remove<'a, OutputService: OutputServiceInterface, ImapService: ImapServiceInterface>(
|
||||
uid: &'a str,
|
||||
flags: Vec<&'a str>,
|
||||
output: &'a OutputService,
|
||||
imap: &'a mut ImapService,
|
||||
) -> Result<()> {
|
||||
let flags = Flags::from(flags);
|
||||
imap.add_flags(uid, flags)?;
|
||||
imap.remove_flags(uid, &flags)?;
|
||||
output.print(format!(
|
||||
r#"Flag(s) "{}" successfully removed from message {}"#,
|
||||
flags, uid
|
||||
))?;
|
||||
imap.logout()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove<ImapService: ImapServiceInterface>(
|
||||
uid: &str,
|
||||
flags: &str,
|
||||
imap: &mut ImapService,
|
||||
/// Replace flags from the given message UID sequence.
|
||||
/// Flags do not need to be prefixed with `\` and they are not case-sensitive.
|
||||
///
|
||||
/// ```ignore
|
||||
/// set("21", "\\Seen", &output, &mut imap)?;
|
||||
/// set("42", "recent", &output, &mut imap)?;
|
||||
/// set("1:10", "Answered custom", &output, &mut imap)?;
|
||||
/// ```
|
||||
pub fn set<'a, OutputService: OutputServiceInterface, ImapService: ImapServiceInterface>(
|
||||
uid: &'a str,
|
||||
flags: Vec<&'a str>,
|
||||
output: &'a OutputService,
|
||||
imap: &'a mut ImapService,
|
||||
) -> Result<()> {
|
||||
let flags = Flags::from(flags);
|
||||
imap.remove_flags(uid, flags)?;
|
||||
imap.set_flags(uid, &flags)?;
|
||||
output.print(format!(
|
||||
r#"Flag(s) "{}" successfully set for message {}"#,
|
||||
flags, uid
|
||||
))?;
|
||||
imap.logout()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//! Module related to message handling.
|
||||
//!
|
||||
//! This module gathers all message commands.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use atty::Stream;
|
||||
use imap::types::Flag;
|
||||
|
@ -175,7 +179,7 @@ pub fn delete<OutputService: OutputServiceInterface, ImapService: ImapServiceInt
|
|||
imap: &mut ImapService,
|
||||
) -> Result<()> {
|
||||
let flags = Flags::from(vec![Flag::Seen, Flag::Deleted]);
|
||||
imap.add_flags(uid, flags)?;
|
||||
imap.add_flags(uid, &flags)?;
|
||||
imap.expunge()?;
|
||||
output.print(format!("Message(s) {} successfully deleted", uid))?;
|
||||
imap.logout()?;
|
||||
|
@ -288,8 +292,8 @@ pub fn move_<ImapService: ImapServiceInterface>(
|
|||
uid, target
|
||||
))?;
|
||||
// delete the msg in the old mailbox
|
||||
let flags = vec![Flag::Seen, Flag::Deleted];
|
||||
imap.add_flags(uid, Flags::from(flags))?;
|
||||
let flags = Flags::from(vec![Flag::Seen, Flag::Deleted]);
|
||||
imap.add_flags(uid, &flags)?;
|
||||
imap.expunge()?;
|
||||
imap.logout()?;
|
||||
Ok(())
|
||||
|
|
24
src/lib.rs
24
src/lib.rs
|
@ -1,24 +0,0 @@
|
|||
//! # Welcome to Himalaya!
|
||||
//! Here's a little summary of how to read the code of himalaya:
|
||||
//! Each module includes three "main" files:
|
||||
//! - `model.rs`: **The "main" file** of each module which includes the main implementation of the given
|
||||
//! module.
|
||||
//! - `cli.rs`: Includes the subcommands and arguments which are related to the module.
|
||||
//!
|
||||
//! For example the `read` subcommand is in the `msg/cli.rs` file because it's related to the
|
||||
//! msg you want to read.
|
||||
//!
|
||||
//! - `mod.rs`: Includes all other files in the module. Click [here] for more information.
|
||||
//!
|
||||
//! [here]: https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html
|
||||
|
||||
pub mod compl;
|
||||
|
||||
/// Everything which is related to the config files. For example the structure of your config file.
|
||||
pub mod config;
|
||||
|
||||
/// Handles the output. For example the JSON and HTML output.
|
||||
pub mod output;
|
||||
|
||||
pub mod domain;
|
||||
pub mod ui;
|
32
src/main.rs
32
src/main.rs
|
@ -4,20 +4,20 @@ use env_logger;
|
|||
use std::{convert::TryFrom, env};
|
||||
use url::Url;
|
||||
|
||||
use himalaya::{
|
||||
compl,
|
||||
config::{
|
||||
self,
|
||||
entity::{Account, Config},
|
||||
},
|
||||
domain::{
|
||||
imap::{self, service::ImapService},
|
||||
mbox::{self, entity::Mbox},
|
||||
msg,
|
||||
smtp::service::SmtpService,
|
||||
},
|
||||
output::{self, service::OutputService},
|
||||
mod compl;
|
||||
mod config;
|
||||
mod domain;
|
||||
mod output;
|
||||
mod ui;
|
||||
|
||||
use config::entity::{Account, Config};
|
||||
use domain::{
|
||||
imap::{self, service::ImapService},
|
||||
mbox::{self, entity::Mbox},
|
||||
msg,
|
||||
smtp::service::SmtpService,
|
||||
};
|
||||
use output::service::OutputService;
|
||||
|
||||
fn create_app<'a>() -> clap::App<'a, 'a> {
|
||||
clap::App::new(env!("CARGO_PKG_NAME"))
|
||||
|
@ -132,13 +132,13 @@ fn main() -> Result<()> {
|
|||
|
||||
Some(msg::arg::Command::Flag(m)) => match m {
|
||||
Some(msg::flag::arg::Command::Set(uid, flags)) => {
|
||||
return msg::flag::handler::set(uid, flags, &mut imap);
|
||||
return msg::flag::handler::set(uid, flags, &output, &mut imap);
|
||||
}
|
||||
Some(msg::flag::arg::Command::Add(uid, flags)) => {
|
||||
return msg::flag::handler::add(uid, flags, &mut imap);
|
||||
return msg::flag::handler::add(uid, flags, &output, &mut imap);
|
||||
}
|
||||
Some(msg::flag::arg::Command::Remove(uid, flags)) => {
|
||||
return msg::flag::handler::remove(uid, flags, &mut imap);
|
||||
return msg::flag::handler::remove(uid, flags, &output, &mut imap);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
|
|
|
@ -67,17 +67,6 @@ pub struct OutputService {
|
|||
}
|
||||
|
||||
impl OutputService {
|
||||
/// Create a new output-handler by setting the given formatting style.
|
||||
pub fn new(slice: &str) -> Result<Self> {
|
||||
let fmt = OutputFmt::try_from(Some(slice))?;
|
||||
Ok(Self { fmt })
|
||||
}
|
||||
|
||||
/// Returns true, if the formatting should be plaintext.
|
||||
pub fn is_plain(&self) -> bool {
|
||||
self.fmt == OutputFmt::Plain
|
||||
}
|
||||
|
||||
/// Returns true, if the formatting should be json.
|
||||
pub fn is_json(&self) -> bool {
|
||||
self.fmt == OutputFmt::Json
|
||||
|
|
Loading…
Reference in a new issue