mirror of
https://github.com/pimalaya/himalaya.git
synced 2026-06-17 05:07:55 +08:00
improve global options, add account config sync-folders-strategy
This commit is contained in:
Vendored
+6
-1
@@ -8,8 +8,13 @@ const ARG_DISABLE_CACHE: &str = "disable-cache";
|
||||
/// the user to disable any sort of cache.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_DISABLE_CACHE)
|
||||
.long("disable-cache")
|
||||
.help("Disable any sort of cache")
|
||||
.long_help(
|
||||
"Disable any sort of cache. The action depends on
|
||||
the command it applies on.",
|
||||
)
|
||||
.long("disable-cache")
|
||||
.global(true)
|
||||
.action(ArgAction::SetTrue)
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -8,9 +8,10 @@ const ARG_CONFIG: &str = "config";
|
||||
/// user to customize the config file path.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_CONFIG)
|
||||
.help("Set a custom configuration file path")
|
||||
.long("config")
|
||||
.short('c')
|
||||
.help("Forces a specific config file path")
|
||||
.global(true)
|
||||
.value_name("PATH")
|
||||
}
|
||||
|
||||
|
||||
+16
-4
@@ -1,9 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use himalaya_lib::{
|
||||
EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig, SendmailConfig, SmtpConfig,
|
||||
folder::sync::Strategy as SyncFoldersStrategy, EmailHooks, EmailSender, EmailTextPlainFormat,
|
||||
MaildirConfig, SendmailConfig, SmtpConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya_lib::ImapConfig;
|
||||
@@ -111,3 +111,15 @@ pub struct EmailHooksDef {
|
||||
/// Represents the hook called just before sending an email.
|
||||
pub pre_send: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(remote = "SyncFoldersStrategy", rename_all = "kebab-case")]
|
||||
pub enum SyncFoldersStrategyDef {
|
||||
#[default]
|
||||
All,
|
||||
#[serde(alias = "only")]
|
||||
Include(HashSet<String>),
|
||||
#[serde(alias = "except")]
|
||||
#[serde(alias = "ignore")]
|
||||
Exclude(HashSet<String>),
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Arg, ArgAction, ArgMatches, Command};
|
||||
use himalaya_lib::folder::sync::Strategy as SyncFoldersStrategy;
|
||||
use log::info;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::ui::table;
|
||||
use crate::{folder, ui::table};
|
||||
|
||||
const ARG_ACCOUNT: &str = "account";
|
||||
const ARG_DRY_RUN: &str = "dry-run";
|
||||
@@ -20,7 +22,7 @@ pub enum Cmd {
|
||||
/// Represents the list accounts command.
|
||||
List(table::args::MaxTableWidth),
|
||||
/// Represents the sync account command.
|
||||
Sync(DryRun),
|
||||
Sync(Option<SyncFoldersStrategy>, DryRun),
|
||||
}
|
||||
|
||||
/// Represents the account command matcher.
|
||||
@@ -29,7 +31,22 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||
if let Some(m) = m.subcommand_matches(CMD_SYNC) {
|
||||
info!("sync account subcommand matched");
|
||||
let dry_run = parse_dry_run_arg(m);
|
||||
Some(Cmd::Sync(dry_run))
|
||||
let include = folder::args::parse_include_arg(m);
|
||||
let exclude = folder::args::parse_exclude_arg(m);
|
||||
let folders_strategy = if let Some(folder) = folder::args::parse_source_arg(m) {
|
||||
Some(SyncFoldersStrategy::Include(HashSet::from_iter([
|
||||
folder.to_owned()
|
||||
])))
|
||||
} else if !include.is_empty() {
|
||||
Some(SyncFoldersStrategy::Include(include.to_owned()))
|
||||
} else if !exclude.is_empty() {
|
||||
Some(SyncFoldersStrategy::Exclude(exclude))
|
||||
} else if folder::args::parse_all_arg(m) {
|
||||
Some(SyncFoldersStrategy::All)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Cmd::Sync(folders_strategy, dry_run))
|
||||
} else if let Some(m) = m.subcommand_matches(CMD_LIST) {
|
||||
info!("list accounts subcommand matched");
|
||||
let max_table_width = table::args::parse_max_width(m);
|
||||
@@ -55,6 +72,13 @@ pub fn subcmd() -> Command {
|
||||
.arg(table::args::max_width()),
|
||||
Command::new(CMD_SYNC)
|
||||
.about("Synchronize the given account locally")
|
||||
.arg(folder::args::all_arg("Synchronize all folders"))
|
||||
.arg(folder::args::include_arg(
|
||||
"Synchronize only the given folders",
|
||||
))
|
||||
.arg(folder::args::exclude_arg(
|
||||
"Synchronize all folders except the given ones",
|
||||
))
|
||||
.arg(dry_run()),
|
||||
])
|
||||
}
|
||||
@@ -63,9 +87,10 @@ pub fn subcmd() -> Command {
|
||||
/// the user to select a different account than the default one.
|
||||
pub fn arg() -> Arg {
|
||||
Arg::new(ARG_ACCOUNT)
|
||||
.help("Set the account")
|
||||
.long("account")
|
||||
.short('a')
|
||||
.help("Select a specific account by name")
|
||||
.global(true)
|
||||
.value_name("STRING")
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
//! account in the accounts section of the user configuration file.
|
||||
|
||||
use himalaya_lib::{
|
||||
AccountConfig, BackendConfig, EmailHooks, EmailSender, EmailTextPlainFormat, MaildirConfig,
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, BackendConfig, EmailHooks,
|
||||
EmailSender, EmailTextPlainFormat, MaildirConfig,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
#[cfg(feature = "imap-backend")]
|
||||
use himalaya_lib::ImapConfig;
|
||||
@@ -13,9 +16,6 @@ use himalaya_lib::ImapConfig;
|
||||
#[cfg(feature = "notmuch-backend")]
|
||||
use himalaya_lib::NotmuchConfig;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use crate::config::{prelude::*, DeserializedConfig};
|
||||
|
||||
/// Represents all existing kind of account config.
|
||||
@@ -104,6 +104,8 @@ pub struct DeserializedBaseAccountConfig {
|
||||
#[serde(default)]
|
||||
pub sync: bool,
|
||||
pub sync_dir: Option<PathBuf>,
|
||||
#[serde(default, with = "SyncFoldersStrategyDef")]
|
||||
pub sync_folders_strategy: SyncFoldersStrategy,
|
||||
}
|
||||
|
||||
impl DeserializedBaseAccountConfig {
|
||||
@@ -207,6 +209,7 @@ impl DeserializedBaseAccountConfig {
|
||||
},
|
||||
sync: self.sync,
|
||||
sync_dir: self.sync_dir.clone(),
|
||||
sync_folders_strategy: self.sync_folders_strategy.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
//! This module gathers all account actions triggered by the CLI.
|
||||
|
||||
use anyhow::Result;
|
||||
use himalaya_lib::{AccountConfig, Backend, BackendSyncBuilder, BackendSyncProgressEvent};
|
||||
use himalaya_lib::{
|
||||
folder::sync::Strategy as SyncFoldersStrategy, AccountConfig, Backend, BackendSyncBuilder,
|
||||
BackendSyncProgressEvent,
|
||||
};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use log::{info, trace};
|
||||
|
||||
@@ -43,15 +46,17 @@ pub fn sync<P: Printer>(
|
||||
account_config: &AccountConfig,
|
||||
printer: &mut P,
|
||||
backend: &dyn Backend,
|
||||
folder: &Option<String>,
|
||||
folders_strategy: Option<SyncFoldersStrategy>,
|
||||
dry_run: bool,
|
||||
) -> Result<()> {
|
||||
info!("entering the sync accounts handler");
|
||||
trace!("dry run: {}", dry_run);
|
||||
trace!("dry run: {dry_run}");
|
||||
trace!("folders strategy: {folders_strategy:#?}");
|
||||
|
||||
let mut sync_builder = BackendSyncBuilder::new(account_config);
|
||||
if let Some(folder) = folder {
|
||||
sync_builder = sync_builder.only_folder(folder);
|
||||
|
||||
if let Some(strategy) = folders_strategy {
|
||||
sync_builder = sync_builder.folders_strategy(strategy);
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
|
||||
@@ -3,12 +3,17 @@
|
||||
//! This module provides subcommands, arguments and a command matcher
|
||||
//! related to the folder domain.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{self, Arg, ArgMatches, Command};
|
||||
use clap::{self, Arg, ArgAction, ArgMatches, Command};
|
||||
use log::{debug, info};
|
||||
|
||||
use crate::ui::table;
|
||||
|
||||
const ARG_ALL: &str = "all";
|
||||
const ARG_EXCLUDE: &str = "exclude";
|
||||
const ARG_INCLUDE: &str = "include";
|
||||
const ARG_SOURCE: &str = "source";
|
||||
const ARG_TARGET: &str = "target";
|
||||
const CMD_CREATE: &str = "create";
|
||||
@@ -74,9 +79,10 @@ pub fn subcmd() -> Command {
|
||||
/// Represents the source folder argument.
|
||||
pub fn source_arg() -> Arg {
|
||||
Arg::new(ARG_SOURCE)
|
||||
.help("Set the source folder")
|
||||
.long("folder")
|
||||
.short('f')
|
||||
.help("Specifies the source folder")
|
||||
.global(true)
|
||||
.value_name("SOURCE")
|
||||
}
|
||||
|
||||
@@ -85,6 +91,70 @@ pub fn parse_source_arg(matches: &ArgMatches) -> Option<&str> {
|
||||
matches.get_one::<String>(ARG_SOURCE).map(String::as_str)
|
||||
}
|
||||
|
||||
/// Represents the all folders argument.
|
||||
pub fn all_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_ALL)
|
||||
.help(help)
|
||||
.long("all-folders")
|
||||
.alias("all")
|
||||
.short('A')
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the all folders argument parser.
|
||||
pub fn parse_all_arg(m: &ArgMatches) -> bool {
|
||||
m.get_flag(ARG_ALL)
|
||||
}
|
||||
|
||||
/// Represents the folders to include argument.
|
||||
pub fn include_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_INCLUDE)
|
||||
.help(help)
|
||||
.long("include-folder")
|
||||
.alias("only")
|
||||
.short('F')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_EXCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to include argument parser.
|
||||
pub fn parse_include_arg(m: &ArgMatches) -> HashSet<String> {
|
||||
m.get_many::<String>(ARG_INCLUDE)
|
||||
.unwrap_or_default()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument.
|
||||
pub fn exclude_arg(help: &'static str) -> Arg {
|
||||
Arg::new(ARG_EXCLUDE)
|
||||
.help(help)
|
||||
.long("exclude-folder")
|
||||
.alias("except")
|
||||
.short('x')
|
||||
.value_name("FOLDER")
|
||||
.num_args(1..)
|
||||
.action(ArgAction::Append)
|
||||
.conflicts_with(ARG_SOURCE)
|
||||
.conflicts_with(ARG_ALL)
|
||||
.conflicts_with(ARG_INCLUDE)
|
||||
}
|
||||
|
||||
/// Represents the folders to exclude argument parser.
|
||||
pub fn parse_exclude_arg(m: &ArgMatches) -> HashSet<String> {
|
||||
m.get_many::<String>(ARG_EXCLUDE)
|
||||
.unwrap_or_default()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Represents the target folder argument.
|
||||
pub fn target_arg() -> Arg {
|
||||
Arg::new(ARG_TARGET)
|
||||
|
||||
+2
-6
@@ -122,11 +122,7 @@ fn main() -> Result<()> {
|
||||
Some(account::args::Cmd::List(max_width)) => {
|
||||
return account::handlers::list(max_width, &account_config, &config, &mut printer);
|
||||
}
|
||||
Some(account::args::Cmd::Sync(dry_run)) => {
|
||||
let folder = match folder {
|
||||
Some(folder) => Some(account_config.folder_alias(folder)?),
|
||||
None => None,
|
||||
};
|
||||
Some(account::args::Cmd::Sync(folders_strategy, dry_run)) => {
|
||||
let backend = BackendBuilder::new()
|
||||
.sessions_pool_size(8)
|
||||
.disable_cache(true)
|
||||
@@ -135,7 +131,7 @@ fn main() -> Result<()> {
|
||||
&account_config,
|
||||
&mut printer,
|
||||
backend.as_ref(),
|
||||
&folder,
|
||||
folders_strategy,
|
||||
dry_run,
|
||||
)?;
|
||||
backend.close()?;
|
||||
|
||||
+6
-3
@@ -30,11 +30,14 @@ pub fn matches(m: &ArgMatches) -> Result<Option<Cmd>> {
|
||||
/// Man subcommands.
|
||||
pub fn subcmd() -> Command {
|
||||
Command::new(CMD_MAN)
|
||||
.about("Generates all man pages to the specified directory.")
|
||||
.about("Generate all man pages to the given directory")
|
||||
.arg(
|
||||
Arg::new(ARG_DIR)
|
||||
.help("Directory where to generate man files")
|
||||
.long_help("Represents the directory where all man files of all commands and subcommands should be generated in.")
|
||||
.help("Directory to generate man files in")
|
||||
.long_help(
|
||||
"Represents the directory where all man files of
|
||||
all commands and subcommands should be generated in.",
|
||||
)
|
||||
.required(true),
|
||||
)
|
||||
}
|
||||
|
||||
+12
-11
@@ -11,22 +11,23 @@ pub(crate) const ARG_OUTPUT: &str = "output";
|
||||
pub fn args() -> Vec<Arg> {
|
||||
vec![
|
||||
Arg::new(ARG_OUTPUT)
|
||||
.help("Defines the output format")
|
||||
.help("Set the output format")
|
||||
.long("output")
|
||||
.short('o')
|
||||
.global(true)
|
||||
.value_name("FMT")
|
||||
.value_parser(["plain", "json"])
|
||||
.default_value("plain"),
|
||||
Arg::new(ARG_COLOR)
|
||||
.help("Controls when to use colors.")
|
||||
.help("Control when to use colors.")
|
||||
.long_help(
|
||||
"
|
||||
This flag controls when to use colors. The default setting is 'auto', which
|
||||
means himalaya will try to guess when to use colors. For example, if himalaya is
|
||||
printing to a terminal, then it will use colors, but if it is redirected to a
|
||||
file or a pipe, then it will suppress color output. himalaya will suppress color
|
||||
output in some other circumstances as well. For example, if the TERM
|
||||
environment variable is not set or set to 'dumb', then himalaya will not use
|
||||
"This flag controls when to use colors. The default
|
||||
setting is 'auto', which means himalaya will try to guess when to use
|
||||
colors. For example, if himalaya is printing to a terminal, then it
|
||||
will use colors, but if it is redirected to a file or a pipe, then it
|
||||
will suppress color output. himalaya will suppress color output in
|
||||
some other circumstances as well. For example, if the TERM environment
|
||||
variable is not set or set to 'dumb', then himalaya will not use
|
||||
colors.
|
||||
|
||||
The possible values for this flag are:
|
||||
@@ -34,11 +35,11 @@ The possible values for this flag are:
|
||||
never Colors will never be used.
|
||||
auto The default. himalaya tries to be smart.
|
||||
always Colors will always be used regardless of where output is sent.
|
||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).
|
||||
",
|
||||
ansi Like 'always', but emits ANSI escapes (even in a Windows console).",
|
||||
)
|
||||
.long("color")
|
||||
.short('C')
|
||||
.global(true)
|
||||
.value_parser(["never", "auto", "always", "ansi"])
|
||||
.default_value("auto")
|
||||
.value_name("WHEN"),
|
||||
|
||||
Reference in New Issue
Block a user