mirror of
https://github.com/soywod/himalaya.git
synced 2024-11-24 20:10:23 +00:00
bump deps, make global config option repeatable
This commit is contained in:
parent
3868c62511
commit
7ee710634b
10 changed files with 374 additions and 359 deletions
|
@ -13,9 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- Made the global `--config|-c` option repeatable: the first option is considered the path to the main config, and successive options are considered partial overrides [#184].
|
||||||
- Changed the `envelope list` options (see `envelope list --help` for more details):
|
- Changed the `envelope list` options (see `envelope list --help` for more details):
|
||||||
- the folder argument became a flag `--folder <name>`
|
- The folder argument became a flag `--folder <name>`.
|
||||||
- the query argument has been added at the end of the command to filter and sort results [#39]
|
- The query argument has been added at the end of the command to filter and sort results [#39].
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -807,4 +808,5 @@ Few major concepts changed:
|
||||||
[#95]: https://todo.sr.ht/~soywod/pimalaya/95
|
[#95]: https://todo.sr.ht/~soywod/pimalaya/95
|
||||||
[#172]: https://todo.sr.ht/~soywod/pimalaya/172
|
[#172]: https://todo.sr.ht/~soywod/pimalaya/172
|
||||||
[#173]: https://todo.sr.ht/~soywod/pimalaya/173
|
[#173]: https://todo.sr.ht/~soywod/pimalaya/173
|
||||||
|
[#184]: https://todo.sr.ht/~soywod/pimalaya/184
|
||||||
[#188]: https://todo.sr.ht/~soywod/pimalaya/188
|
[#188]: https://todo.sr.ht/~soywod/pimalaya/188
|
||||||
|
|
446
Cargo.lock
generated
446
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
33
Cargo.toml
33
Cargo.toml
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "himalaya"
|
name = "himalaya"
|
||||||
description = "CLI to manage emails"
|
description = "CLI to manage emails"
|
||||||
version = "1.0.0-beta.3"
|
version = "1.0.0-beta.4"
|
||||||
authors = ["soywod <clement.douin@posteo.net>"]
|
authors = ["soywod <clement.douin@posteo.net>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
@ -13,7 +13,7 @@ repository = "https://github.com/soywod/himalaya/"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs", "--document-private-items"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [
|
default = [
|
||||||
|
@ -46,7 +46,6 @@ pgp-gpg = ["email-lib/pgp-gpg", "mml-lib/pgp-gpg", "pgp"]
|
||||||
pgp-native = ["email-lib/pgp-native", "mml-lib/pgp-native", "pgp"]
|
pgp-native = ["email-lib/pgp-native", "mml-lib/pgp-native", "pgp"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
async-trait = "0.1"
|
|
||||||
tempfile = "3.3"
|
tempfile = "3.3"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -60,29 +59,30 @@ clap_mangen = "0.2"
|
||||||
console = "0.15.2"
|
console = "0.15.2"
|
||||||
dialoguer = "0.10.2"
|
dialoguer = "0.10.2"
|
||||||
dirs = "4"
|
dirs = "4"
|
||||||
email-lib = { version = "=0.22.3", default-features = false }
|
email-lib = { version = "=0.22.3", default-features = false, features = ["derive"] }
|
||||||
email_address = "0.2.4"
|
email_address = "0.2.4"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
erased-serde = "0.3"
|
erased-serde = "0.3"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
keyring-lib = "=0.3.2"
|
keyring-lib = { version = "=0.4.0", features = ["derive"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mail-builder = "0.3"
|
mail-builder = "0.3"
|
||||||
md5 = "0.7"
|
md5 = "0.7"
|
||||||
mml-lib = { version = "=1.0.7", default-features = false }
|
mml-lib = { version = "=1.0.8", default-features = false, features = ["derive"] }
|
||||||
oauth-lib = "=0.1.0"
|
oauth-lib = "=0.1.0"
|
||||||
once_cell = "1.16"
|
once_cell = "1.16"
|
||||||
process-lib = "=0.3.1"
|
process-lib = { version = "=0.4.1", features = ["derive"] }
|
||||||
secret-lib = "=0.3.3"
|
secret-lib = { version = "=0.4.1", features = ["derive"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde-toml-merge = "0.3"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
shellexpand-utils = "=0.2.0"
|
shellexpand-utils = "=0.2.0"
|
||||||
sled = "=0.34.7"
|
sled = "=0.34.7"
|
||||||
termcolor = "1"
|
termcolor = "1"
|
||||||
terminal_size = "0.1"
|
terminal_size = "0.1"
|
||||||
tokio = { version = "1.23", default-features = false, features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.23", default-features = false, features = ["macros", "rt-multi-thread"] }
|
||||||
toml = "0.7.4"
|
toml = "0.8"
|
||||||
toml_edit = "0.19.8"
|
toml_edit = "0.22"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uuid = { version = "0.8", features = ["v4"] }
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
|
@ -91,5 +91,14 @@ uuid = { version = "0.8", features = ["v4"] }
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
email-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
# waiting for alpha 7
|
||||||
chumsky = { git = "https://github.com/zesterer/chumsky.git", rev = "6837537" }
|
chumsky = { git = "https://github.com/zesterer/chumsky.git", rev = "6837537" }
|
||||||
|
email-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
email-macros = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
keyring-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
mml-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
oauth-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
pgp-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
process-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
secret-lib = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
shellexpand-utils = { git = "https://git.sr.ht/~soywod/pimalaya" }
|
||||||
|
|
37
src/cli.rs
37
src/cli.rs
|
@ -25,17 +25,19 @@ pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Option<HimalayaCommand>,
|
pub command: Option<HimalayaCommand>,
|
||||||
|
|
||||||
/// Override the default configuration file path
|
/// Override the default configuration file path.
|
||||||
///
|
///
|
||||||
/// The given path is shell-expanded then canonicalized (if
|
/// The given paths are shell-expanded then canonicalized (if
|
||||||
/// applicable). If the path does not point to a valid file, the
|
/// applicable). If the first path does not point to a valid file,
|
||||||
/// wizard will propose to assist you in the creation of the
|
/// the wizard will propose to assist you in the creation of the
|
||||||
/// configuration file.
|
/// configuration file. Other paths are merged with the first one,
|
||||||
|
/// which allows you to separate your public config from your
|
||||||
|
/// private(s) one(s).
|
||||||
#[arg(short, long = "config", global = true)]
|
#[arg(short, long = "config", global = true)]
|
||||||
#[arg(value_name = "PATH", value_parser = config::path_parser)]
|
#[arg(value_name = "PATH", value_parser = config::path_parser)]
|
||||||
pub config_path: Option<PathBuf>,
|
pub config_paths: Vec<PathBuf>,
|
||||||
|
|
||||||
/// Customize the output format
|
/// Customize the output format.
|
||||||
///
|
///
|
||||||
/// The output format determine how to display commands output to
|
/// The output format determine how to display commands output to
|
||||||
/// the terminal.
|
/// the terminal.
|
||||||
|
@ -116,39 +118,34 @@ pub enum HimalayaCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HimalayaCommand {
|
impl HimalayaCommand {
|
||||||
#[allow(unused)]
|
pub async fn execute(self, printer: &mut impl Printer, config_paths: &[PathBuf]) -> Result<()> {
|
||||||
pub async fn execute(
|
|
||||||
self,
|
|
||||||
printer: &mut impl Printer,
|
|
||||||
config_path: Option<&PathBuf>,
|
|
||||||
) -> Result<()> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Account(cmd) => {
|
Self::Account(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Folder(cmd) => {
|
Self::Folder(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Envelope(cmd) => {
|
Self::Envelope(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Flag(cmd) => {
|
Self::Flag(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Message(cmd) => {
|
Self::Message(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Attachment(cmd) => {
|
Self::Attachment(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Template(cmd) => {
|
Self::Template(cmd) => {
|
||||||
let config = TomlConfig::from_some_path_or_default(config_path).await?;
|
let config = TomlConfig::from_paths_or_default(config_paths).await?;
|
||||||
cmd.execute(printer, &config).await
|
cmd.execute(printer, &config).await
|
||||||
}
|
}
|
||||||
Self::Manual(cmd) => cmd.execute(printer).await,
|
Self::Manual(cmd) => cmd.execute(printer).await,
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
pub mod wizard;
|
pub mod wizard;
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use dirs::{config_dir, home_dir};
|
use dirs::{config_dir, home_dir};
|
||||||
use email::{
|
use email::{
|
||||||
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig,
|
account::config::AccountConfig, config::Config, envelope::config::EnvelopeConfig,
|
||||||
flag::config::FlagConfig, folder::config::FolderConfig, message::config::MessageConfig,
|
flag::config::FlagConfig, folder::config::FolderConfig, message::config::MessageConfig,
|
||||||
};
|
};
|
||||||
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_toml_merge::merge;
|
||||||
use shellexpand_utils::{canonicalize, expand};
|
use shellexpand_utils::{canonicalize, expand};
|
||||||
use std::{
|
use std::{collections::HashMap, fs, path::PathBuf, sync::Arc};
|
||||||
collections::HashMap,
|
use toml::{self, Value};
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
use toml;
|
|
||||||
|
|
||||||
#[cfg(feature = "account-sync")]
|
#[cfg(feature = "account-sync")]
|
||||||
use crate::backend::BackendKind;
|
use crate::backend::BackendKind;
|
||||||
|
@ -34,14 +30,48 @@ pub struct TomlConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TomlConfig {
|
impl TomlConfig {
|
||||||
/// Read and parse the TOML configuration at the given path.
|
/// Read and parse the TOML configuration at the given paths.
|
||||||
///
|
///
|
||||||
/// Returns an error if the configuration file cannot be read or
|
/// Returns an error if a configuration file cannot be read or if
|
||||||
/// if its content cannot be parsed.
|
/// a content cannot be parsed.
|
||||||
fn from_path(path: &Path) -> Result<Self> {
|
fn from_paths(paths: &[PathBuf]) -> Result<Self> {
|
||||||
let content =
|
match paths.len() {
|
||||||
fs::read_to_string(path).context(format!("cannot read config file at {path:?}"))?;
|
0 => {
|
||||||
toml::from_str(&content).context(format!("cannot parse config file at {path:?}"))
|
// should never happen
|
||||||
|
bail!("cannot read config file from empty paths");
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let path = &paths[0];
|
||||||
|
|
||||||
|
let ref content = fs::read_to_string(path)
|
||||||
|
.context(format!("cannot read config file at {path:?}"))?;
|
||||||
|
|
||||||
|
toml::from_str(content).context(format!("cannot parse config file at {path:?}"))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let path = &paths[0];
|
||||||
|
|
||||||
|
let mut merged_content = fs::read_to_string(path)
|
||||||
|
.context(format!("cannot read config file at {path:?}"))?
|
||||||
|
.parse::<Value>()?;
|
||||||
|
|
||||||
|
for path in &paths[1..] {
|
||||||
|
match fs::read_to_string(path) {
|
||||||
|
Ok(content) => {
|
||||||
|
merged_content = merge(merged_content, content.parse()?).unwrap();
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!("skipping subconfig file at {path:?}: {err}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
merged_content
|
||||||
|
.try_into()
|
||||||
|
.context(format!("cannot parse merged config file at {path:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create and save a TOML configuration using the wizard.
|
/// Create and save a TOML configuration using the wizard.
|
||||||
|
@ -51,7 +81,7 @@ impl TomlConfig {
|
||||||
/// program stops.
|
/// program stops.
|
||||||
///
|
///
|
||||||
/// NOTE: the wizard can only be used with interactive shells.
|
/// NOTE: the wizard can only be used with interactive shells.
|
||||||
async fn from_wizard(path: PathBuf) -> Result<Self> {
|
async fn from_wizard(path: &PathBuf) -> Result<Self> {
|
||||||
use dialoguer::Confirm;
|
use dialoguer::Confirm;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
|
@ -75,8 +105,8 @@ impl TomlConfig {
|
||||||
/// Read and parse the TOML configuration from default paths.
|
/// Read and parse the TOML configuration from default paths.
|
||||||
pub async fn from_default_paths() -> Result<Self> {
|
pub async fn from_default_paths() -> Result<Self> {
|
||||||
match Self::first_valid_default_path() {
|
match Self::first_valid_default_path() {
|
||||||
Some(path) => Self::from_path(&path),
|
Some(path) => Self::from_paths(&[path]),
|
||||||
None => Self::from_wizard(Self::default_path()?).await,
|
None => Self::from_wizard(&Self::default_path()?).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +122,11 @@ impl TomlConfig {
|
||||||
/// If no path is given, then either read and parse the TOML
|
/// If no path is given, then either read and parse the TOML
|
||||||
/// configuration at the first valid default path, otherwise
|
/// configuration at the first valid default path, otherwise
|
||||||
/// create it using the wizard. wizard.
|
/// create it using the wizard. wizard.
|
||||||
pub async fn from_some_path_or_default(path: Option<impl Into<PathBuf>>) -> Result<Self> {
|
pub async fn from_paths_or_default(paths: &[PathBuf]) -> Result<Self> {
|
||||||
match path.map(Into::into) {
|
match paths.len() {
|
||||||
Some(ref path) if path.exists() => Self::from_path(path),
|
0 => Self::from_default_paths().await,
|
||||||
Some(path) => Self::from_wizard(path).await,
|
_ if paths[0].exists() => Self::from_paths(paths),
|
||||||
_ => Self::from_default_paths().await,
|
_ => Self::from_wizard(&paths[0]).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,14 +187,14 @@ impl TomlConfig {
|
||||||
if let Some(imap_config) = toml_account_config.imap.as_mut() {
|
if let Some(imap_config) = toml_account_config.imap.as_mut() {
|
||||||
imap_config
|
imap_config
|
||||||
.auth
|
.auth
|
||||||
.replace_undefined_keyring_entries(&account_name);
|
.replace_undefined_keyring_entries(&account_name)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "smtp")]
|
#[cfg(feature = "smtp")]
|
||||||
if let Some(smtp_config) = toml_account_config.smtp.as_mut() {
|
if let Some(smtp_config) = toml_account_config.smtp.as_mut() {
|
||||||
smtp_config
|
smtp_config
|
||||||
.auth
|
.auth
|
||||||
.replace_undefined_keyring_entries(&account_name);
|
.replace_undefined_keyring_entries(&account_name)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((account_name, toml_account_config))
|
Ok((account_name, toml_account_config))
|
||||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
||||||
use dialoguer::{Confirm, Input, Select};
|
use dialoguer::{Confirm, Input, Select};
|
||||||
use shellexpand_utils::expand;
|
use shellexpand_utils::expand;
|
||||||
use std::{fs, path::PathBuf, process};
|
use std::{fs, path::PathBuf, process};
|
||||||
use toml_edit::{Document, Item};
|
use toml_edit::{DocumentMut, Item};
|
||||||
|
|
||||||
use crate::{account, ui::THEME};
|
use crate::{account, ui::THEME};
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ macro_rules! wizard_log {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
|
pub(crate) async fn configure(path: &PathBuf) -> Result<TomlConfig> {
|
||||||
wizard_log!("Configuring your first account:");
|
wizard_log!("Configuring your first account:");
|
||||||
|
|
||||||
let mut config = TomlConfig::default();
|
let mut config = TomlConfig::default();
|
||||||
|
@ -103,7 +103,7 @@ pub(crate) async fn configure(path: PathBuf) -> Result<TomlConfig> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pretty_serialize(config: &TomlConfig) -> Result<String> {
|
fn pretty_serialize(config: &TomlConfig) -> Result<String> {
|
||||||
let mut doc: Document = toml::to_string(&config)?.parse()?;
|
let mut doc: DocumentMut = toml::to_string(&config)?.parse()?;
|
||||||
|
|
||||||
doc.iter_mut().for_each(|(_, item)| {
|
doc.iter_mut().for_each(|(_, item)| {
|
||||||
if let Some(item) = item.as_table_mut() {
|
if let Some(item) = item.as_table_mut() {
|
||||||
|
@ -299,7 +299,9 @@ folder.sync.permissions.delete = true
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 143,
|
port: 143,
|
||||||
login: "test@localhost".into(),
|
login: "test@localhost".into(),
|
||||||
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_cmd("pass show test"))),
|
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_command(
|
||||||
|
"pass show test",
|
||||||
|
))),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -330,7 +332,7 @@ imap.passwd.cmd = "pass show test"
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 143,
|
port: 143,
|
||||||
login: "test@localhost".into(),
|
login: "test@localhost".into(),
|
||||||
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_cmd(vec![
|
auth: ImapAuthConfig::Passwd(PasswdConfig(Secret::new_command(vec![
|
||||||
"pass show test",
|
"pass show test",
|
||||||
"tr -d '[:blank:]'",
|
"tr -d '[:blank:]'",
|
||||||
]))),
|
]))),
|
||||||
|
@ -424,7 +426,9 @@ maildir.root-dir = "/tmp/test"
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 143,
|
port: 143,
|
||||||
login: "test@localhost".into(),
|
login: "test@localhost".into(),
|
||||||
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_cmd("pass show test"))),
|
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_command(
|
||||||
|
"pass show test",
|
||||||
|
))),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -455,7 +459,7 @@ smtp.passwd.cmd = "pass show test"
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 143,
|
port: 143,
|
||||||
login: "test@localhost".into(),
|
login: "test@localhost".into(),
|
||||||
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_cmd(vec![
|
auth: SmtpAuthConfig::Passwd(PasswdConfig(Secret::new_command(vec![
|
||||||
"pass show test",
|
"pass show test",
|
||||||
"tr -d '[:blank:]'",
|
"tr -d '[:blank:]'",
|
||||||
]))),
|
]))),
|
||||||
|
|
|
@ -163,10 +163,10 @@ pub(crate) async fn configure(
|
||||||
.with_prompt("IMAP OAuth 2.0 client secret")
|
.with_prompt("IMAP OAuth 2.0 client secret")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
config.client_secret =
|
config.client_secret =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))?;
|
||||||
config
|
config
|
||||||
.client_secret
|
.client_secret
|
||||||
.set_keyring_entry_secret(&client_secret)
|
.set_only_keyring(&client_secret)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let default_auth_url = autoconfig_oauth2
|
let default_auth_url = autoconfig_oauth2
|
||||||
|
@ -278,19 +278,13 @@ pub(crate) async fn configure(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.access_token =
|
config.access_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"))?;
|
||||||
config
|
config.access_token.set_only_keyring(access_token).await?;
|
||||||
.access_token
|
|
||||||
.set_keyring_entry_secret(access_token)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(refresh_token) = &refresh_token {
|
if let Some(refresh_token) = &refresh_token {
|
||||||
config.refresh_token =
|
config.refresh_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))?;
|
||||||
config
|
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||||
.refresh_token
|
|
||||||
.set_keyring_entry_secret(refresh_token)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImapAuthConfig::OAuth2(config)
|
ImapAuthConfig::OAuth2(config)
|
||||||
|
@ -303,14 +297,14 @@ pub(crate) async fn configure(
|
||||||
|
|
||||||
let secret = match secret_idx {
|
let secret = match secret_idx {
|
||||||
Some(idx) if SECRETS[idx] == KEYRING => {
|
Some(idx) if SECRETS[idx] == KEYRING => {
|
||||||
let secret = Secret::new_keyring_entry(format!("{account_name}-imap-passwd"));
|
let secret = Secret::try_new_keyring_entry(format!("{account_name}-imap-passwd"))?;
|
||||||
secret
|
secret
|
||||||
.set_keyring_entry_secret(prompt::passwd("IMAP password")?)
|
.set_only_keyring(prompt::passwd("IMAP password")?)
|
||||||
.await?;
|
.await?;
|
||||||
secret
|
secret
|
||||||
}
|
}
|
||||||
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("IMAP password")?),
|
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("IMAP password")?),
|
||||||
Some(idx) if SECRETS[idx] == CMD => Secret::new_cmd(
|
Some(idx) if SECRETS[idx] == CMD => Secret::new_command(
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.with_prompt("Shell command")
|
.with_prompt("Shell command")
|
||||||
.default(format!("pass show {account_name}-imap-passwd"))
|
.default(format!("pass show {account_name}-imap-passwd"))
|
||||||
|
@ -404,10 +398,10 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
.with_prompt("IMAP OAuth 2.0 client secret")
|
.with_prompt("IMAP OAuth 2.0 client secret")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
config.client_secret =
|
config.client_secret =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-client-secret"))?;
|
||||||
config
|
config
|
||||||
.client_secret
|
.client_secret
|
||||||
.set_keyring_entry_secret(&client_secret)
|
.set_only_keyring(&client_secret)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.auth_url = Input::with_theme(&*THEME)
|
config.auth_url = Input::with_theme(&*THEME)
|
||||||
|
@ -500,19 +494,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.access_token =
|
config.access_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-access-token"))?;
|
||||||
config
|
config.access_token.set_only_keyring(access_token).await?;
|
||||||
.access_token
|
|
||||||
.set_keyring_entry_secret(access_token)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(refresh_token) = &refresh_token {
|
if let Some(refresh_token) = &refresh_token {
|
||||||
config.refresh_token =
|
config.refresh_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-imap-oauth2-refresh-token"))?;
|
||||||
config
|
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||||
.refresh_token
|
|
||||||
.set_keyring_entry_secret(refresh_token)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImapAuthConfig::OAuth2(config)
|
ImapAuthConfig::OAuth2(config)
|
||||||
|
@ -525,14 +513,14 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
|
|
||||||
let secret = match secret_idx {
|
let secret = match secret_idx {
|
||||||
Some(idx) if SECRETS[idx] == KEYRING => {
|
Some(idx) if SECRETS[idx] == KEYRING => {
|
||||||
let secret = Secret::new_keyring_entry(format!("{account_name}-imap-passwd"));
|
let secret = Secret::try_new_keyring_entry(format!("{account_name}-imap-passwd"))?;
|
||||||
secret
|
secret
|
||||||
.set_keyring_entry_secret(prompt::passwd("IMAP password")?)
|
.set_only_keyring(prompt::passwd("IMAP password")?)
|
||||||
.await?;
|
.await?;
|
||||||
secret
|
secret
|
||||||
}
|
}
|
||||||
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("IMAP password")?),
|
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("IMAP password")?),
|
||||||
Some(idx) if SECRETS[idx] == CMD => Secret::new_cmd(
|
Some(idx) if SECRETS[idx] == CMD => Secret::new_command(
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.with_prompt("Shell command")
|
.with_prompt("Shell command")
|
||||||
.default(format!("pass show {account_name}-imap-passwd"))
|
.default(format!("pass show {account_name}-imap-passwd"))
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -22,10 +22,11 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
// if the first argument starts by "mailto:", execute straight the
|
// if the first argument starts by "mailto:", execute straight the
|
||||||
// mailto message command
|
// mailto message command
|
||||||
if let Some(ref url) = std::env::args()
|
let mailto = std::env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.filter(|arg| arg.starts_with("mailto:"))
|
.filter(|arg| arg.starts_with("mailto:"));
|
||||||
{
|
|
||||||
|
if let Some(ref url) = mailto {
|
||||||
let mut printer = StdoutPrinter::default();
|
let mut printer = StdoutPrinter::default();
|
||||||
let config = TomlConfig::from_default_paths().await?;
|
let config = TomlConfig::from_default_paths().await?;
|
||||||
|
|
||||||
|
@ -38,9 +39,9 @@ async fn main() -> Result<()> {
|
||||||
let mut printer = StdoutPrinter::new(cli.output, cli.color);
|
let mut printer = StdoutPrinter::new(cli.output, cli.color);
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Some(cmd) => cmd.execute(&mut printer, cli.config_path.as_ref()).await,
|
Some(cmd) => cmd.execute(&mut printer, cli.config_paths.as_ref()).await,
|
||||||
None => {
|
None => {
|
||||||
let config = TomlConfig::from_some_path_or_default(cli.config_path.as_ref()).await?;
|
let config = TomlConfig::from_paths_or_default(cli.config_paths.as_ref()).await?;
|
||||||
ListEnvelopesCommand::default()
|
ListEnvelopesCommand::default()
|
||||||
.execute(&mut printer, &config)
|
.execute(&mut printer, &config)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -163,10 +163,10 @@ pub(crate) async fn configure(
|
||||||
.with_prompt("SMTP OAuth 2.0 client secret")
|
.with_prompt("SMTP OAuth 2.0 client secret")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
config.client_secret =
|
config.client_secret =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"))?;
|
||||||
config
|
config
|
||||||
.client_secret
|
.client_secret
|
||||||
.set_keyring_entry_secret(&client_secret)
|
.set_only_keyring(&client_secret)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let default_auth_url = autoconfig_oauth2
|
let default_auth_url = autoconfig_oauth2
|
||||||
|
@ -278,19 +278,13 @@ pub(crate) async fn configure(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.access_token =
|
config.access_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"))?;
|
||||||
config
|
config.access_token.set_only_keyring(access_token).await?;
|
||||||
.access_token
|
|
||||||
.set_keyring_entry_secret(access_token)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(refresh_token) = &refresh_token {
|
if let Some(refresh_token) = &refresh_token {
|
||||||
config.refresh_token =
|
config.refresh_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))?;
|
||||||
config
|
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||||
.refresh_token
|
|
||||||
.set_keyring_entry_secret(refresh_token)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SmtpAuthConfig::OAuth2(config)
|
SmtpAuthConfig::OAuth2(config)
|
||||||
|
@ -303,14 +297,14 @@ pub(crate) async fn configure(
|
||||||
|
|
||||||
let secret = match secret_idx {
|
let secret = match secret_idx {
|
||||||
Some(idx) if SECRETS[idx] == KEYRING => {
|
Some(idx) if SECRETS[idx] == KEYRING => {
|
||||||
let secret = Secret::new_keyring_entry(format!("{account_name}-smtp-passwd"));
|
let secret = Secret::try_new_keyring_entry(format!("{account_name}-smtp-passwd"))?;
|
||||||
secret
|
secret
|
||||||
.set_keyring_entry_secret(prompt::passwd("SMTP password")?)
|
.set_only_keyring(prompt::passwd("SMTP password")?)
|
||||||
.await?;
|
.await?;
|
||||||
secret
|
secret
|
||||||
}
|
}
|
||||||
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("SMTP password")?),
|
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("SMTP password")?),
|
||||||
Some(idx) if SECRETS[idx] == CMD => Secret::new_cmd(
|
Some(idx) if SECRETS[idx] == CMD => Secret::new_command(
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.with_prompt("Shell command")
|
.with_prompt("Shell command")
|
||||||
.default(format!("pass show {account_name}-smtp-passwd"))
|
.default(format!("pass show {account_name}-smtp-passwd"))
|
||||||
|
@ -403,10 +397,10 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
.with_prompt("SMTP OAuth 2.0 client secret")
|
.with_prompt("SMTP OAuth 2.0 client secret")
|
||||||
.interact()?;
|
.interact()?;
|
||||||
config.client_secret =
|
config.client_secret =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-client-secret"))?;
|
||||||
config
|
config
|
||||||
.client_secret
|
.client_secret
|
||||||
.set_keyring_entry_secret(&client_secret)
|
.set_only_keyring(&client_secret)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.auth_url = Input::with_theme(&*THEME)
|
config.auth_url = Input::with_theme(&*THEME)
|
||||||
|
@ -499,19 +493,13 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
config.access_token =
|
config.access_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-access-token"))?;
|
||||||
config
|
config.access_token.set_only_keyring(access_token).await?;
|
||||||
.access_token
|
|
||||||
.set_keyring_entry_secret(access_token)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(refresh_token) = &refresh_token {
|
if let Some(refresh_token) = &refresh_token {
|
||||||
config.refresh_token =
|
config.refresh_token =
|
||||||
Secret::new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"));
|
Secret::try_new_keyring_entry(format!("{account_name}-smtp-oauth2-refresh-token"))?;
|
||||||
config
|
config.refresh_token.set_only_keyring(refresh_token).await?;
|
||||||
.refresh_token
|
|
||||||
.set_keyring_entry_secret(refresh_token)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SmtpAuthConfig::OAuth2(config)
|
SmtpAuthConfig::OAuth2(config)
|
||||||
|
@ -524,14 +512,14 @@ pub(crate) async fn configure(account_name: &str, email: &str) -> Result<Backend
|
||||||
|
|
||||||
let secret = match secret_idx {
|
let secret = match secret_idx {
|
||||||
Some(idx) if SECRETS[idx] == KEYRING => {
|
Some(idx) if SECRETS[idx] == KEYRING => {
|
||||||
let secret = Secret::new_keyring_entry(format!("{account_name}-smtp-passwd"));
|
let secret = Secret::try_new_keyring_entry(format!("{account_name}-smtp-passwd"))?;
|
||||||
secret
|
secret
|
||||||
.set_keyring_entry_secret(prompt::passwd("SMTP password")?)
|
.set_only_keyring(prompt::passwd("SMTP password")?)
|
||||||
.await?;
|
.await?;
|
||||||
secret
|
secret
|
||||||
}
|
}
|
||||||
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("SMTP password")?),
|
Some(idx) if SECRETS[idx] == RAW => Secret::new_raw(prompt::passwd("SMTP password")?),
|
||||||
Some(idx) if SECRETS[idx] == CMD => Secret::new_cmd(
|
Some(idx) if SECRETS[idx] == CMD => Secret::new_command(
|
||||||
Input::with_theme(&*THEME)
|
Input::with_theme(&*THEME)
|
||||||
.with_prompt("Shell command")
|
.with_prompt("Shell command")
|
||||||
.default(format!("pass show {account_name}-smtp-passwd"))
|
.default(format!("pass show {account_name}-smtp-passwd"))
|
||||||
|
|
|
@ -8,7 +8,7 @@ use email::{
|
||||||
};
|
};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use mml::MmlCompilerBuilder;
|
use mml::MmlCompilerBuilder;
|
||||||
use process::SingleCmd;
|
use process::SingleCommand;
|
||||||
use std::{env, fs, sync::Arc};
|
use std::{env, fs, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -25,7 +25,7 @@ pub async fn open_with_tpl(tpl: String) -> Result<String> {
|
||||||
|
|
||||||
debug!("open editor");
|
debug!("open editor");
|
||||||
let editor = env::var("EDITOR").context("cannot get editor from env var")?;
|
let editor = env::var("EDITOR").context("cannot get editor from env var")?;
|
||||||
SingleCmd::from(format!("{editor} {}", &path.to_string_lossy()))
|
SingleCommand::from(format!("{editor} {}", &path.to_string_lossy()))
|
||||||
.with_output_piped(false)
|
.with_output_piped(false)
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
Loading…
Reference in a new issue