1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
//! `init` subcommand
use abscissa_core::{status_err, Command, Runnable, Shutdown};
use anyhow::{bail, Result};
use dialoguer::Password;
use crate::{commands::get_repository, Application, RUSTIC_APP};
use rustic_core::{ConfigOptions, KeyOptions, OpenStatus, Repository};
/// `init` subcommand
#[derive(clap::Parser, Command, Debug)]
pub(crate) struct InitCmd {
/// Key options
#[clap(flatten, next_help_heading = "Key options")]
key_opts: KeyOptions,
/// Config options
#[clap(flatten, next_help_heading = "Config options")]
config_opts: ConfigOptions,
}
impl Runnable for InitCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}
impl InitCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = get_repository(&config.repository)?;
// Note: This is again checked in repo.init_with_password(), however we want to inform
// users before they are prompted to enter a password
if repo.config_id()?.is_some() {
bail!("Config file already exists. Aborting.");
}
// Handle dry-run mode
if config.global.dry_run {
bail!(
"cannot initialize repository {} in dry-run mode!",
repo.name
);
}
let _ = init(repo, &self.key_opts, &self.config_opts)?;
Ok(())
}
}
/// Initialize repository
///
/// # Arguments
///
/// * `repo` - Repository to initialize
/// * `key_opts` - Key options
/// * `config_opts` - Config options
///
/// # Errors
///
/// * [`RepositoryErrorKind::OpeningPasswordFileFailed`] - If opening the password file failed
/// * [`RepositoryErrorKind::ReadingPasswordFromReaderFailed`] - If reading the password failed
/// * [`RepositoryErrorKind::FromSplitError`] - If splitting the password command failed
/// * [`RepositoryErrorKind::PasswordCommandParsingFailed`] - If parsing the password command failed
/// * [`RepositoryErrorKind::ReadingPasswordFromCommandFailed`] - If reading the password from the command failed
///
/// # Returns
///
/// Returns the initialized repository
///
/// [`RepositoryErrorKind::OpeningPasswordFileFailed`]: rustic_core::error::RepositoryErrorKind::OpeningPasswordFileFailed
/// [`RepositoryErrorKind::ReadingPasswordFromReaderFailed`]: rustic_core::error::RepositoryErrorKind::ReadingPasswordFromReaderFailed
/// [`RepositoryErrorKind::FromSplitError`]: rustic_core::error::RepositoryErrorKind::FromSplitError
/// [`RepositoryErrorKind::PasswordCommandParsingFailed`]: rustic_core::error::RepositoryErrorKind::PasswordCommandParsingFailed
/// [`RepositoryErrorKind::ReadingPasswordFromCommandFailed`]: rustic_core::error::RepositoryErrorKind::ReadingPasswordFromCommandFailed
pub(crate) fn init<P, S>(
repo: Repository<P, S>,
key_opts: &KeyOptions,
config_opts: &ConfigOptions,
) -> Result<Repository<P, OpenStatus>> {
let pass = init_password(&repo)?;
Ok(repo.init_with_password(&pass, key_opts, config_opts)?)
}
pub(crate) fn init_password<P, S>(repo: &Repository<P, S>) -> Result<String> {
let pass = repo.password()?.unwrap_or_else(|| {
match Password::new()
.with_prompt("enter password for new key")
.allow_empty_password(true)
.with_confirmation("confirm password", "passwords do not match")
.interact()
{
Ok(it) => it,
Err(err) => {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
}
}
});
Ok(pass)
}