diff --git a/meli/src/command.rs b/meli/src/command.rs index 0f55aad0..b4e22199 100644 --- a/meli/src/command.rs +++ b/meli/src/command.rs @@ -55,10 +55,11 @@ pub use crate::actions::{ AccountAction::{self, *}, Action::{self, *}, ComposeAction::{self, *}, + FlagAction, ListingAction::{self, *}, MailingListAction::{self, *}, TabAction::{self, *}, - TagAction::{self, *}, + TagAction, ViewAction::{self, *}, }; diff --git a/meli/src/command/actions.rs b/meli/src/command/actions.rs index 4bb7ed9c..a8f93753 100644 --- a/meli/src/command/actions.rs +++ b/meli/src/command/actions.rs @@ -23,10 +23,16 @@ use std::path::PathBuf; -use melib::{email::mailto::Mailto, SortField, SortOrder}; +use melib::{email::mailto::Mailto, Flag, SortField, SortOrder}; use crate::components::{Component, ComponentId}; +#[derive(Debug)] +pub enum FlagAction { + Set(Flag), + Unset(Flag), +} + #[derive(Debug)] pub enum TagAction { Add(String), @@ -52,6 +58,7 @@ pub enum ListingAction { Delete, OpenInNewTab, Tag(TagAction), + Flag(FlagAction), ToggleThreadSnooze, } diff --git a/meli/src/command/parser.rs b/meli/src/command/parser.rs index 40d9dd7f..5cd663a6 100644 --- a/meli/src/command/parser.rs +++ b/meli/src/command/parser.rs @@ -101,6 +101,7 @@ pub fn listing_action(input: &[u8]) -> IResult<&[u8], Result Result { .and_then(|(_, v)| v) } +/// Set/unset a flag. +/// +/// # Example +/// +/// ``` +/// # use meli::{melib::Flag, command::{Action,ListingAction, FlagAction, parser}}; +/// +/// let (rest, parsed) = parser::flag(b"flag set junk").unwrap(); +/// assert_eq!(rest, b""); +/// assert!( +/// matches!( +/// parsed, +/// Ok(Action::Listing(ListingAction::Flag(FlagAction::Set( +/// Flag::TRASHED +/// )))) +/// ), +/// "{:?}", +/// parsed +/// ); +/// +/// let (rest, parsed) = parser::flag(b"flag unset junk").unwrap(); +/// assert_eq!(rest, b""); +/// assert!( +/// matches!( +/// parsed, +/// Ok(Action::Listing(ListingAction::Flag(FlagAction::Unset( +/// Flag::TRASHED +/// )))) +/// ), +/// "{:?}", +/// parsed +/// ); +/// +/// let (rest, parsed) = parser::flag(b"flag set draft").unwrap(); +/// assert_eq!(rest, b""); +/// assert!( +/// matches!( +/// parsed, +/// Ok(Action::Listing(ListingAction::Flag(FlagAction::Set( +/// Flag::DRAFT +/// )))) +/// ), +/// "{:?}", +/// parsed +/// ); +/// +/// let (rest, parsed) = parser::flag(b"flag set xunk").unwrap(); +/// assert_eq!(rest, b""); +/// assert_eq!( +/// &parsed.unwrap_err().to_string(), +/// "Bad value/argument: xunk is not a valid flag name. Possible values are: passed, replied, \ +/// seen or read, junk or trash or trashed, draft and flagged." +/// ); +/// ``` +pub fn flag<'a>(input: &'a [u8]) -> IResult<&'a [u8], Result> { + use melib::Flag; + + fn parse_flag(s: &str) -> Option { + match s { + o if o.eq_ignore_ascii_case("passed") => Some(Flag::PASSED), + o if o.eq_ignore_ascii_case("replied") => Some(Flag::REPLIED), + o if o.eq_ignore_ascii_case("seen") => Some(Flag::SEEN), + o if o.eq_ignore_ascii_case("read") => Some(Flag::SEEN), + o if o.eq_ignore_ascii_case("junk") => Some(Flag::TRASHED), + o if o.eq_ignore_ascii_case("trash") => Some(Flag::TRASHED), + o if o.eq_ignore_ascii_case("trashed") => Some(Flag::TRASHED), + o if o.eq_ignore_ascii_case("draft") => Some(Flag::DRAFT), + o if o.eq_ignore_ascii_case("flagged") => Some(Flag::FLAGGED), + _ => None, + } + } + + preceded( + tag("flag"), + alt(( + |input: &'a [u8]| -> IResult<&'a [u8], Result> { + let mut check = arg_init! { min_arg:2, max_arg: 2, flag}; + let (input, _) = tag("set")(input.trim())?; + arg_chk!(start check, input); + let (input, _) = is_a(" ")(input)?; + arg_chk!(inc check, input); + let (input, flag) = quoted_argument(input.trim())?; + arg_chk!(finish check, input); + let (input, _) = eof(input)?; + let Some(flag) = parse_flag(flag) else { + return Ok(( + b"", + Err(CommandError::BadValue { + inner: format!( + "{flag} is not a valid flag name. Possible values are: passed, \ + replied, seen or read, junk or trash or trashed, draft and \ + flagged." + ) + .into(), + }), + )); + }; + Ok((input, Ok(Listing(Flag(FlagAction::Set(flag)))))) + }, + |input: &'a [u8]| -> IResult<&'a [u8], Result> { + let mut check = arg_init! { min_arg:2, max_arg: 2, flag}; + let (input, _) = tag("unset")(input.trim())?; + arg_chk!(start check, input); + let (input, _) = is_a(" ")(input)?; + arg_chk!(inc check, input); + let (input, flag) = quoted_argument(input.trim())?; + arg_chk!(finish check, input); + let (input, _) = eof(input)?; + let Some(flag) = parse_flag(flag) else { + return Ok(( + b"", + Err(CommandError::BadValue { + inner: format!( + "{flag} is not a valid flag name. Possible values are: passed, \ + replied, seen or read, junk or trash or trashed, draft and \ + flagged." + ) + .into(), + }), + )); + }; + Ok((input, Ok(Listing(Flag(FlagAction::Unset(flag)))))) + }, + )), + )(input.trim()) +} + pub fn set(input: &[u8]) -> IResult<&[u8], Result> { fn toggle(input: &[u8]) -> IResult<&[u8], Result> { let mut check = arg_init! { min_arg:1, max_arg: 1, set}; @@ -708,6 +836,19 @@ pub fn add_addresses_to_contacts(input: &[u8]) -> IResult<&[u8], Result(input: &'a [u8]) -> IResult<&'a [u8], Result> { preceded( tag("tag"), @@ -721,7 +862,7 @@ pub fn _tag<'a>(input: &'a [u8]) -> IResult<&'a [u8], Result IResult<&'a [u8], Result> { let mut check = arg_init! { min_arg:2, max_arg: 2, tag}; @@ -732,11 +873,12 @@ pub fn _tag<'a>(input: &'a [u8]) -> IResult<&'a [u8], Result IResult<&[u8], Result> { let mut check = arg_init! { min_arg:2, max_arg: 2, print}; let (input, _) = tag("print")(input.trim())?; diff --git a/meli/src/mail/listing.rs b/meli/src/mail/listing.rs index 53241cd7..fa321793 100644 --- a/meli/src/mail/listing.rs +++ b/meli/src/mail/listing.rs @@ -546,7 +546,7 @@ pub trait MailListingTrait: ListingTrait { )); } } - ListingAction::Tag(Add(ref tag_str)) => { + ListingAction::Tag(TagAction::Add(ref tag_str)) => { if let Err(err) = account.set_flags( env_hashes.clone(), mailbox_hash, @@ -557,7 +557,7 @@ pub trait MailListingTrait: ListingTrait { )); } } - ListingAction::Tag(Remove(ref tag_str)) => { + ListingAction::Tag(TagAction::Remove(ref tag_str)) => { if let Err(err) = account.set_flags( env_hashes.clone(), mailbox_hash,