use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
use std::path::PathBuf;
use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::Result;
use dialoguer::Password;
use log::info;
use rustic_core::{KeyOptions, RepositoryOptions};
#[derive(clap::Parser, Command, Debug)]
pub(super) struct KeyCmd {
#[clap(subcommand)]
cmd: KeySubCmd,
}
#[derive(clap::Subcommand, Debug, Runnable)]
enum KeySubCmd {
Add(AddCmd),
}
#[derive(clap::Parser, Debug)]
pub(crate) struct AddCmd {
#[clap(long)]
pub(crate) new_password: Option<String>,
#[clap(long)]
pub(crate) new_password_file: Option<PathBuf>,
#[clap(long)]
pub(crate) new_password_command: Vec<String>,
#[clap(flatten)]
pub(crate) key_opts: KeyOptions,
}
impl Runnable for KeyCmd {
fn run(&self) {
self.cmd.run();
}
}
impl Runnable for AddCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}
impl AddCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?;
let pass_opts = RepositoryOptions {
password: self.new_password.clone(),
password_file: self.new_password_file.clone(),
password_command: self.new_password_command.clone(),
..Default::default()
};
let pass = pass_opts
.evaluate_password()
.map_err(|err| err.into())
.transpose()
.unwrap_or_else(|| -> Result<_> {
Ok(Password::new()
.with_prompt("enter password for new key")
.allow_empty_password(true)
.with_confirmation("confirm password", "passwords do not match")
.interact()?)
})?;
let id = repo.add_key(&pass, &self.key_opts)?;
info!("key {id} successfully added.");
Ok(())
}
}