Skip to main content

rustic_rs/commands/
init.rs

1//! `init` subcommand
2
3use abscissa_core::{Command, Runnable, Shutdown, status_err};
4use anyhow::{Result, bail};
5use dialoguer::Password;
6
7use crate::{
8    Application, RUSTIC_APP,
9    repository::{OpenRepo, Repo},
10};
11
12use rustic_core::{ConfigOptions, CredentialOptions, Credentials, KeyOptions};
13
14/// `init` subcommand
15#[derive(clap::Parser, Command, Debug)]
16pub(crate) struct InitCmd {
17    /// initialize hot repository for existing cold repository
18    #[clap(long)]
19    hot_only: bool,
20
21    /// Key options
22    #[clap(flatten, next_help_heading = "Key options")]
23    key_opts: KeyOptions,
24
25    /// Config options
26    #[clap(flatten, next_help_heading = "Config options")]
27    config_opts: ConfigOptions,
28}
29
30impl Runnable for InitCmd {
31    fn run(&self) {
32        if let Err(err) = RUSTIC_APP
33            .config()
34            .repository
35            .run(|repo| self.inner_run(repo))
36        {
37            status_err!("{}", err);
38            RUSTIC_APP.shutdown(Shutdown::Crash);
39        };
40    }
41}
42
43impl InitCmd {
44    fn inner_run(&self, repo: Repo) -> Result<()> {
45        let config = RUSTIC_APP.config();
46
47        // Handle dry-run mode
48        if config.global.dry_run {
49            bail!(
50                "cannot initialize repository {} in dry-run mode!",
51                repo.name
52            );
53        }
54
55        if self.hot_only {
56            if config.repository.be.repo_hot.is_none() {
57                bail!("please specify a hot repository");
58            }
59            let repo = repo
60                .open_with(&config.repository.credential_opts, |repo, credentials| {
61                    repo.open_only_cold(credentials)
62                })?;
63            repo.init_hot()?;
64            repo.repair_hotcold_except_packs(config.global.dry_run)?;
65            repo.repair_hotcold_packs(config.global.dry_run)?;
66
67            return Ok(());
68        }
69
70        // Note: This is again checked in init(), however we want to inform
71        // users before they are prompted to enter a password
72        if repo.config_id()?.is_some() {
73            bail!("Config file already exists. Aborting.");
74        }
75
76        let _ = init(
77            repo,
78            &config.repository.credential_opts,
79            &self.key_opts,
80            &self.config_opts,
81        )?;
82        Ok(())
83    }
84}
85
86/// Initialize repository
87///
88/// # Arguments
89///
90/// * `repo` - Repository to initialize
91/// * `credential_opts` - Credential options
92/// * `key_opts` - Key options (only used when generating a new key)
93/// * `config_opts` - Config options
94///
95/// # Errors
96///
97/// * If getting the credentials from the options fails
98///
99/// # Returns
100///
101/// Returns the initialized repository
102pub(crate) fn init(
103    repo: Repo,
104    credential_opts: &CredentialOptions,
105    key_opts: &KeyOptions,
106    config_opts: &ConfigOptions,
107) -> Result<OpenRepo> {
108    let pass = init_credentials(credential_opts)?;
109    Ok(repo.0.init(&pass, key_opts, config_opts)?)
110}
111
112pub(crate) fn init_credentials(credential_opts: &CredentialOptions) -> Result<Credentials> {
113    let credentials = credential_opts.credentials()?.unwrap_or_else(|| {
114        match Password::new()
115            .with_prompt("enter password for new key")
116            .allow_empty_password(true)
117            .with_confirmation("confirm password", "passwords do not match")
118            .interact()
119        {
120            Ok(pass) => Credentials::Password(pass),
121            Err(err) => {
122                status_err!("{}", err);
123                RUSTIC_APP.shutdown(Shutdown::Crash);
124            }
125        }
126    });
127
128    Ok(credentials)
129}