Skip to main content

canic_cli/backup/options/
mod.rs

1use super::{BackupCommandError, usage};
2use clap::{Arg, ArgAction, ArgMatches, Command as ClapCommand};
3use std::{ffi::OsString, path::PathBuf};
4
5///
6/// BackupPreflightOptions
7///
8
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct BackupPreflightOptions {
11    pub dir: PathBuf,
12    pub out_dir: PathBuf,
13    pub mapping: Option<PathBuf>,
14    pub require_design_v1: bool,
15    pub require_restore_ready: bool,
16}
17
18impl BackupPreflightOptions {
19    /// Parse backup preflight options from CLI arguments.
20    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
21    where
22        I: IntoIterator<Item = OsString>,
23    {
24        let matches = parse_backup_options(backup_preflight_command(), args)?;
25
26        Ok(Self {
27            dir: required_path_option(&matches, "dir", "--dir")?,
28            out_dir: required_path_option(&matches, "out-dir", "--out-dir")?,
29            mapping: path_option(&matches, "mapping"),
30            require_design_v1: matches.get_flag("require-design"),
31            require_restore_ready: matches.get_flag("require-restore-ready"),
32        })
33    }
34}
35
36// Build the backup preflight parser.
37fn backup_preflight_command() -> ClapCommand {
38    backup_dir_out_dir_command("backup-preflight")
39        .arg(value_arg("mapping").long("mapping"))
40        .arg(require_design_arg())
41        .arg(flag_arg("require-restore-ready").long("require-restore-ready"))
42}
43
44///
45/// BackupSmokeOptions
46///
47
48#[derive(Clone, Debug, Eq, PartialEq)]
49pub struct BackupSmokeOptions {
50    pub dir: PathBuf,
51    pub out_dir: PathBuf,
52    pub mapping: Option<PathBuf>,
53    pub dfx: String,
54    pub network: Option<String>,
55    pub require_design_v1: bool,
56    pub require_restore_ready: bool,
57}
58
59impl BackupSmokeOptions {
60    /// Parse backup smoke-check options from CLI arguments.
61    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
62    where
63        I: IntoIterator<Item = OsString>,
64    {
65        let matches = parse_backup_options(backup_smoke_command(), args)?;
66
67        Ok(Self {
68            dir: required_path_option(&matches, "dir", "--dir")?,
69            out_dir: required_path_option(&matches, "out-dir", "--out-dir")?,
70            mapping: path_option(&matches, "mapping"),
71            dfx: string_option(&matches, "dfx").unwrap_or_else(|| "dfx".to_string()),
72            network: string_option(&matches, "network"),
73            require_design_v1: matches.get_flag("require-design"),
74            require_restore_ready: matches.get_flag("require-restore-ready"),
75        })
76    }
77}
78
79// Build the backup smoke parser.
80fn backup_smoke_command() -> ClapCommand {
81    backup_dir_out_dir_command("backup-smoke")
82        .arg(value_arg("mapping").long("mapping"))
83        .arg(value_arg("dfx").long("dfx"))
84        .arg(value_arg("network").long("network"))
85        .arg(require_design_arg())
86        .arg(flag_arg("require-restore-ready").long("require-restore-ready"))
87}
88
89///
90/// BackupInspectOptions
91///
92
93#[derive(Clone, Debug, Eq, PartialEq)]
94pub struct BackupInspectOptions {
95    pub dir: PathBuf,
96    pub out: Option<PathBuf>,
97    pub require_ready: bool,
98}
99
100impl BackupInspectOptions {
101    /// Parse backup inspection options from CLI arguments.
102    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
103    where
104        I: IntoIterator<Item = OsString>,
105    {
106        let matches = parse_backup_options(backup_inspect_command(), args)?;
107
108        Ok(Self {
109            dir: required_path_option(&matches, "dir", "--dir")?,
110            out: path_option(&matches, "out"),
111            require_ready: matches.get_flag("require-ready"),
112        })
113    }
114}
115
116// Build the backup inspect parser.
117fn backup_inspect_command() -> ClapCommand {
118    backup_dir_out_command("backup-inspect").arg(flag_arg("require-ready").long("require-ready"))
119}
120
121///
122/// BackupProvenanceOptions
123///
124
125#[derive(Clone, Debug, Eq, PartialEq)]
126pub struct BackupProvenanceOptions {
127    pub dir: PathBuf,
128    pub out: Option<PathBuf>,
129    pub require_consistent: bool,
130}
131
132impl BackupProvenanceOptions {
133    /// Parse backup provenance options from CLI arguments.
134    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
135    where
136        I: IntoIterator<Item = OsString>,
137    {
138        let matches = parse_backup_options(backup_provenance_command(), args)?;
139
140        Ok(Self {
141            dir: required_path_option(&matches, "dir", "--dir")?,
142            out: path_option(&matches, "out"),
143            require_consistent: matches.get_flag("require-consistent"),
144        })
145    }
146}
147
148// Build the backup provenance parser.
149fn backup_provenance_command() -> ClapCommand {
150    backup_dir_out_command("backup-provenance")
151        .arg(flag_arg("require-consistent").long("require-consistent"))
152}
153
154///
155/// BackupVerifyOptions
156///
157
158#[derive(Clone, Debug, Eq, PartialEq)]
159pub struct BackupVerifyOptions {
160    pub dir: PathBuf,
161    pub out: Option<PathBuf>,
162}
163
164impl BackupVerifyOptions {
165    /// Parse backup verification options from CLI arguments.
166    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
167    where
168        I: IntoIterator<Item = OsString>,
169    {
170        let matches = parse_backup_options(backup_verify_command(), args)?;
171
172        Ok(Self {
173            dir: required_path_option(&matches, "dir", "--dir")?,
174            out: path_option(&matches, "out"),
175        })
176    }
177}
178
179// Build the backup verify parser.
180fn backup_verify_command() -> ClapCommand {
181    backup_dir_out_command("backup-verify")
182}
183
184///
185/// BackupStatusOptions
186///
187
188#[derive(Clone, Debug, Eq, PartialEq)]
189pub struct BackupStatusOptions {
190    pub dir: PathBuf,
191    pub out: Option<PathBuf>,
192    pub require_complete: bool,
193}
194
195impl BackupStatusOptions {
196    /// Parse backup status options from CLI arguments.
197    pub fn parse<I>(args: I) -> Result<Self, BackupCommandError>
198    where
199        I: IntoIterator<Item = OsString>,
200    {
201        let matches = parse_backup_options(backup_status_command(), args)?;
202
203        Ok(Self {
204            dir: required_path_option(&matches, "dir", "--dir")?,
205            out: path_option(&matches, "out"),
206            require_complete: matches.get_flag("require-complete"),
207        })
208    }
209}
210
211// Build the backup status parser.
212fn backup_status_command() -> ClapCommand {
213    backup_dir_out_command("backup-status")
214        .arg(flag_arg("require-complete").long("require-complete"))
215}
216
217// Parse one backup command option set.
218fn parse_backup_options<I>(command: ClapCommand, args: I) -> Result<ArgMatches, BackupCommandError>
219where
220    I: IntoIterator<Item = OsString>,
221{
222    let name = command.get_name().to_string();
223    command
224        .try_get_matches_from(std::iter::once(OsString::from(name)).chain(args))
225        .map_err(|_| BackupCommandError::Usage(usage()))
226}
227
228// Build the common --dir/--out parser shape.
229fn backup_dir_out_command(name: &'static str) -> ClapCommand {
230    ClapCommand::new(name)
231        .disable_help_flag(true)
232        .arg(value_arg("dir").long("dir"))
233        .arg(value_arg("out").long("out"))
234}
235
236// Build the common --dir/--out-dir parser shape.
237fn backup_dir_out_dir_command(name: &'static str) -> ClapCommand {
238    ClapCommand::new(name)
239        .disable_help_flag(true)
240        .arg(value_arg("dir").long("dir"))
241        .arg(value_arg("out-dir").long("out-dir"))
242}
243
244// Build one string-valued Clap argument.
245fn value_arg(id: &'static str) -> Arg {
246    Arg::new(id).num_args(1)
247}
248
249// Build one boolean Clap argument.
250fn flag_arg(id: &'static str) -> Arg {
251    Arg::new(id).action(ArgAction::SetTrue)
252}
253
254// Build the current design-conformance flag with its legacy alias.
255fn require_design_arg() -> Arg {
256    flag_arg("require-design")
257        .long("require-design")
258        .alias("require-design-v1")
259}
260
261// Read one string option from Clap matches.
262fn string_option(matches: &ArgMatches, id: &str) -> Option<String> {
263    matches.get_one::<String>(id).cloned()
264}
265
266// Read one optional path from Clap matches.
267fn path_option(matches: &ArgMatches, id: &str) -> Option<PathBuf> {
268    string_option(matches, id).map(PathBuf::from)
269}
270
271// Read one required path from Clap matches.
272fn required_path_option(
273    matches: &ArgMatches,
274    id: &str,
275    option: &'static str,
276) -> Result<PathBuf, BackupCommandError> {
277    path_option(matches, id).ok_or(BackupCommandError::MissingOption(option))
278}