use std::time::Duration;
use anyhow::{Context, Result};
use crate::cli::i18n;
use crate::commands::store_op::{StoreMutation, with_store};
use crate::hooks::executor::HookExecutor;
use crate::models::cli::RecipientCommands;
use crate::models::configuration::{Configuration, encrypt_store};
use crate::models::password_store::{PasswordStore, Recipient};
use crate::recipients;
use crate::recipients::public_key;
pub fn dispatch(
command: &RecipientCommands,
configuration: &Configuration,
offline: bool,
) -> Result<()> {
match command {
RecipientCommands::Add(args) => {
let download_timeout =
Duration::from_secs(configuration.key_download_timeout_seconds.unwrap_or(30));
add(
configuration,
args.store_selection.store.as_ref(),
&public_key::get(&args.keys, download_timeout)?,
args.name.as_ref(),
offline,
)
}
RecipientCommands::Remove(args) => remove(
configuration,
args.store_selection.store.as_ref(),
&args.public_key,
args.ignore_unknown,
offline,
),
RecipientCommands::List(args) => {
list(configuration, args.store_selection.store.as_ref(), offline)
}
}
}
fn add(
configuration: &Configuration,
store_name: Option<&String>,
public_keys: &[(String, String)],
name: Option<&String>,
offline: bool,
) -> Result<()> {
let registration = configuration
.select_store(store_name)
.context(i18n::error_no_store_in_configuration())?;
let store_path = registration.path();
let hooks = HookExecutor {
configuration,
registration,
offline,
force: false,
};
let mut store = if store_path.exists() {
hooks.execute_pull_commands()?;
configuration.decrypt_store(registration)?
} else {
PasswordStore::default()
};
for public_key in public_keys {
if let Some(recipient) = store
.recipients
.iter_mut()
.find(|recipient| recipient.public_key == public_key.0)
{
if let Some(name) = name {
name.clone_into(&mut recipient.name);
}
} else {
store.recipients.push(Recipient {
public_key: public_key.0.clone(),
name: name.map_or(&public_key.1, |value| value).clone(),
});
}
}
encrypt_store(registration, &store).context(i18n::error_cannot_encrypt_store())?;
for public_key in public_keys {
i18n::recipient_added(&public_key.0);
}
if configuration.identities.is_empty() && registration.identities.is_empty() {
i18n::no_identities_exist_yet(®istration.name);
}
hooks.execute_push_commands()?;
Ok(())
}
fn remove(
configuration: &Configuration,
store_name: Option<&String>,
public_key: &str,
ignore_unknown: bool,
offline: bool,
) -> Result<()> {
with_store(configuration, store_name, offline, |_, store| {
if store.recipients.len() == 1 && store.recipients[0].public_key == public_key {
anyhow::bail!(
"Cannot remove the last recipient from the store. Please add a new recipient before removing this one."
)
}
if !store
.recipients
.iter()
.any(|recipient| recipient.public_key == public_key)
{
if ignore_unknown {
i18n::recipient_does_not_exist_ignored(public_key);
return Ok(((), StoreMutation::Unchanged));
}
anyhow::bail!(i18n::error_recipient_not_found_in_store());
}
store
.recipients
.retain(|recipient| recipient.public_key != public_key);
i18n::recipient_removed(public_key);
Ok(((), StoreMutation::Modified))
})
}
fn list(configuration: &Configuration, store_name: Option<&String>, offline: bool) -> Result<()> {
with_store(configuration, store_name, offline, |_, store| {
for recipient in &store.recipients {
println!(
"{}",
recipients::format_recipient(&recipient.public_key, &recipient.name)
);
}
Ok(((), StoreMutation::Unchanged))
})
}