improve imap and mbox arg/handler

This commit is contained in:
Clément DOUIN 2021-09-16 16:43:41 +02:00
parent 248240f52d
commit bc5f9045ce
No known key found for this signature in database
GPG key ID: 69C9B9CFFDEE2DEF
15 changed files with 198 additions and 118 deletions

View file

@ -1,21 +1,12 @@
//! Module related to completion arguments.
//!
//! This module provides subcommands and an argument matcher for the completion command.
//! This module provides subcommands and an argument matcher related to completion.
use anyhow::Result;
use clap::{self, App, Arg, ArgMatches, Shell, SubCommand};
use log::debug;
/// Subcommands related to the completion generation.
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
vec![SubCommand::with_name("completion")
.about("Generates the completion script for the given shell")
.args(&[Arg::with_name("shell")
.possible_values(&Shell::variants()[..])
.required(true)])]
}
/// Enumeration of all possible completion matches.
/// Enumeration of all possible matches.
pub enum Match<'a> {
/// Generate completion script for the given shell slice.
Generate(&'a str),
@ -32,3 +23,12 @@ pub fn matches<'a>(m: &'a ArgMatches) -> Result<Option<Match<'a>>> {
Ok(None)
}
/// Completion subcommands.
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
vec![SubCommand::with_name("completion")
.about("Generates the completion script for the given shell")
.args(&[Arg::with_name("shell")
.possible_values(&Shell::variants()[..])
.required(true)])]
}

View file

@ -1,10 +1,41 @@
//! Module related to IMAP arguments.
//!
//! This module provides subcommands and an argument matcher related to IMAP.
use anyhow::Result;
use clap;
use clap::{App, ArgMatches};
use log::debug;
use crate::domain::{config::entity::Config, imap::service::ImapServiceInterface};
/// Enumeration of all possible matches.
pub enum Match {
/// Start the IMAP notify mode with the give keepalive duration.
Notify(u64),
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
/// Start the IMAP watch mode with the give keepalive duration.
Watch(u64),
}
/// IMAP arg matcher.
pub fn matches(m: &ArgMatches) -> Result<Option<Match>> {
if let Some(m) = m.subcommand_matches("notify") {
debug!("notify command matched");
let keepalive = clap::value_t_or_exit!(m.value_of("keepalive"), u64);
debug!("keepalive: {}", keepalive);
return Ok(Some(Match::Notify(keepalive)));
}
if let Some(m) = m.subcommand_matches("watch") {
debug!("watch command matched");
let keepalive = clap::value_t_or_exit!(m.value_of("keepalive"), u64);
debug!("keepalive: {}", keepalive);
return Ok(Some(Match::Watch(keepalive)));
}
Ok(None)
}
/// IMAP subcommands.
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
vec![
clap::SubCommand::with_name("notify")
.about("Notifies when new messages arrive in the given mailbox")
@ -29,34 +60,3 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
),
]
}
pub fn matches<ImapService: ImapServiceInterface>(
arg_matches: &clap::ArgMatches,
config: &Config,
imap: &mut ImapService,
) -> Result<bool> {
if let Some(matches) = arg_matches.subcommand_matches("notify") {
debug!("notify command matched");
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
debug!("keepalive: {}", &keepalive);
imap.notify(&config, keepalive)?;
imap.logout()?;
return Ok(true);
}
if let Some(matches) = arg_matches.subcommand_matches("watch") {
debug!("watch command matched");
let keepalive = clap::value_t_or_exit!(matches.value_of("keepalive"), u64);
debug!("keepalive: {}", &keepalive);
imap.watch(keepalive)?;
imap.logout()?;
return Ok(true);
}
debug!("nothing matched");
Ok(false)
}

View file

@ -0,0 +1,28 @@
//! Module related to IMAP handling.
//!
//! This module gathers all IMAP handlers triggered by the CLI.
use anyhow::Result;
use crate::domain::{config::entity::Config, imap::service::ImapServiceInterface};
/// Notify handler.
pub fn notify<ImapService: ImapServiceInterface>(
keepalive: u64,
config: &Config,
imap: &mut ImapService,
) -> Result<()> {
imap.notify(&config, keepalive)?;
imap.logout()?;
Ok(())
}
/// Watch handler.
pub fn watch<ImapService: ImapServiceInterface>(
keepalive: u64,
imap: &mut ImapService,
) -> Result<()> {
imap.watch(keepalive)?;
imap.logout()?;
Ok(())
}

View file

@ -1,4 +1,5 @@
//! Modules related to IMAP.
//! Module related to IMAP.
pub mod cli;
pub mod arg;
pub mod handler;
pub mod service;

View file

@ -1,3 +1,7 @@
//! Module related to IMAP servicing.
//!
//! This module exposes a service that can interact with IMAP servers.
use anyhow::{anyhow, Context, Result};
use imap;
use log::{debug, trace};

47
src/domain/mbox/arg.rs Normal file
View file

@ -0,0 +1,47 @@
//! Module related to mailboxes arguments.
//!
//! This module provides subcommands and an argument matcher related to mailboxes.
use anyhow::Result;
use clap::{App, Arg, ArgMatches, SubCommand};
use log::debug;
/// Enumeration of all possible matches.
pub enum Match {
/// List all available mailboxes.
List,
}
/// Mailboxes arg matcher.
pub fn matches(m: &ArgMatches) -> Result<Option<Match>> {
if let Some(_) = m.subcommand_matches("mailboxes") {
debug!("mailboxes command matched");
return Ok(Some(Match::List));
}
Ok(None)
}
/// Mailboxes subcommands.
pub fn subcmds<'a>() -> Vec<App<'a, 'a>> {
vec![SubCommand::with_name("mailboxes")
.aliases(&["mailbox", "mboxes", "mbox", "m"])
.about("Lists all mailboxes")]
}
/// Source mailbox arg.
pub fn source_arg<'a>() -> Arg<'a, 'a> {
Arg::with_name("mailbox")
.short("m")
.long("mailbox")
.help("Selects a specific mailbox")
.value_name("MAILBOX")
.default_value("INBOX")
}
/// Target mailbox arg.
pub fn target_arg<'a>() -> Arg<'a, 'a> {
Arg::with_name("target")
.help("Specifies the targetted mailbox")
.value_name("TARGET")
}

View file

@ -1,48 +0,0 @@
use anyhow::Result;
use clap;
use log::{debug, trace};
use crate::{
domain::{imap::service::ImapServiceInterface, mbox::entity::Mboxes},
output::service::{OutputService, OutputServiceInterface},
};
pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
vec![clap::SubCommand::with_name("mailboxes")
.aliases(&["mailbox", "mboxes", "mbox", "m"])
.about("Lists all mailboxes")]
}
pub fn matches<ImapService: ImapServiceInterface>(
arg_matches: &clap::ArgMatches,
output: &OutputService,
imap: &mut ImapService,
) -> Result<bool> {
if let Some(_) = arg_matches.subcommand_matches("mailboxes") {
debug!("mailboxes command matched");
let names = imap.list_mboxes()?;
let mboxes = Mboxes::from(&names);
debug!("mboxes len: {}", mboxes.0.len());
trace!("{:#?}", mboxes);
output.print(mboxes)?;
imap.logout()?;
return Ok(true);
}
Ok(false)
}
pub fn source_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("mailbox")
.short("m")
.long("mailbox")
.help("Selects a specific mailbox")
.value_name("MAILBOX")
.default_value("INBOX")
}
pub fn mbox_target_arg<'a>() -> clap::Arg<'a, 'a> {
clap::Arg::with_name("target")
.help("Specifies the targetted mailbox")
.value_name("TARGET")
}

View file

@ -0,0 +1,25 @@
//! Module related to mailboxes handling.
//!
//! This module gathers all mailboxes actions triggered by the CLI.
use anyhow::Result;
use log::{debug, trace};
use crate::{
domain::{imap::service::ImapServiceInterface, mbox::entity::Mboxes},
output::service::{OutputService, OutputServiceInterface},
};
/// List mailboxes.
pub fn list<ImapService: ImapServiceInterface>(
output: &OutputService,
imap: &mut ImapService,
) -> Result<()> {
let names = imap.list_mboxes()?;
let mboxes = Mboxes::from(&names);
debug!("mailboxes len: {}", mboxes.0.len());
trace!("mailboxes: {:#?}", mboxes);
output.print(mboxes)?;
imap.logout()?;
Ok(())
}

View file

@ -1,2 +1,5 @@
pub mod cli;
//! Module related to mailboxes.
pub mod arg;
pub mod entity;
pub mod handler;

View file

@ -22,7 +22,7 @@ use crate::{
domain::{
account::entity::Account,
imap::service::ImapServiceInterface,
mbox::{cli::mbox_target_arg, entity::Mbox},
mbox::{arg::target_arg, entity::Mbox},
smtp::service::SmtpServiceInterface,
},
flag::model::Flags,
@ -93,12 +93,12 @@ pub fn subcmds<'a>() -> Vec<clap::App<'a, 'a>> {
.aliases(&["cp"])
.about("Copies a message to the targetted mailbox")
.arg(uid_arg())
.arg(mbox_target_arg()),
.arg(target_arg()),
clap::SubCommand::with_name("move")
.aliases(&["mv"])
.about("Moves a message to the targetted mailbox")
.arg(uid_arg())
.arg(mbox_target_arg()),
.arg(target_arg()),
clap::SubCommand::with_name("delete")
.aliases(&["remove", "rm"])
.about("Deletes a message")

View file

@ -18,7 +18,7 @@
///
/// Execute `himalaya help <cmd>` where `<cmd>` is one entry of this list above
/// to get more information about them.
pub mod cli;
pub mod arg;
/// Here are the two **main structs** of this module: `Msg` and `Msgs` which
/// represent a *Mail* or *multiple Mails* in this crate.

View file

@ -3,7 +3,7 @@ use clap;
use log::debug;
use crate::{
domain::{imap::service::ImapServiceInterface, msg::cli::uid_arg},
domain::{imap::service::ImapServiceInterface, msg::arg::uid_arg},
flag::model::Flags,
};

View file

@ -1,2 +1,2 @@
pub mod cli;
pub mod arg;
pub mod model;

View file

@ -23,14 +23,13 @@ fn create_app<'a>() -> clap::App<'a, 'a> {
.version(env!("CARGO_PKG_VERSION"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.author(env!("CARGO_PKG_AUTHORS"))
.setting(clap::AppSettings::InferSubcommands)
.args(&output_args())
.args(&config_args())
.arg(mbox::cli::source_arg())
.subcommands(flag::cli::subcmds())
.subcommands(imap::cli::subcmds())
.subcommands(mbox::cli::subcmds())
.subcommands(msg::cli::subcmds())
.arg(mbox::arg::source_arg())
.subcommands(flag::arg::subcmds())
.subcommands(imap::arg::subcmds())
.subcommands(mbox::arg::subcmds())
.subcommands(msg::arg::subcmds())
.subcommands(compl::arg::subcmds())
}
@ -57,9 +56,13 @@ fn main() -> Result<()> {
let app = create_app();
let m = app.get_matches();
// Check shell completion BEFORE any entity or service initialization.
if let Some(compl::arg::Match::Generate(shell)) = compl::arg::matches(&m)? {
return compl::handler::generate(shell, create_app());
// Check completion match BEFORE any entity or service initialization.
// See https://github.com/soywod/himalaya/issues/115.
match compl::arg::matches(&m)? {
Some(compl::arg::Match::Generate(shell)) => {
return compl::handler::generate(shell, create_app());
}
_ => (),
}
let mbox = Mbox::try_from(m.value_of("mailbox"))?;
@ -69,11 +72,28 @@ fn main() -> Result<()> {
let mut imap = ImapService::from((&account, &mbox));
let mut smtp = SmtpService::from(&account);
// Check IMAP matches.
match imap::arg::matches(&m)? {
Some(imap::arg::Match::Notify(keepalive)) => {
return imap::handler::notify(keepalive, &config, &mut imap);
}
Some(imap::arg::Match::Watch(keepalive)) => {
return imap::handler::watch(keepalive, &mut imap);
}
_ => (),
}
// Check mailbox matches.
match mbox::arg::matches(&m)? {
Some(mbox::arg::Match::List) => {
return mbox::handler::list(&output, &mut imap);
}
_ => (),
}
// TODO: use same system as compl
let _matched = mbox::cli::matches(&m, &output, &mut imap)?
|| flag::cli::matches(&m, &mut imap)?
|| imap::cli::matches(&m, &config, &mut imap)?
|| msg::cli::matches(&m, &mbox, &account, &output, &mut imap, &mut smtp)?;
let _matched = flag::arg::matches(&m, &mut imap)?
|| msg::arg::matches(&m, &mbox, &account, &output, &mut imap, &mut smtp)?;
Ok(())
}

View file

@ -4,7 +4,7 @@
//!
//! [builder design pattern]: https://refactoring.guru/design-patterns/builder
use log::{debug, trace};
use log::trace;
use std::fmt;
use terminal_size;
use unicode_width::UnicodeWidthStr;
@ -228,12 +228,12 @@ where
table
.iter_mut()
.map(|row| {
debug!("processing row: {:?}", row);
trace!("processing row: {:?}", row);
row.0
.iter_mut()
.enumerate()
.map(|(i, cell)| {
debug!("processing cell: {:?}", cell);
trace!("processing cell: {:?}", cell);
trace!("table_width: {}", table_width);
trace!("max_width: {}", Self::max_width());