1mod backup;
2mod build;
3mod cli;
4mod cycles;
5mod deploy;
6mod endpoints;
7mod evidence;
8mod evidence_support;
9mod fleets;
10mod info;
11mod info_env;
12mod install;
13mod list;
14mod medic;
15mod metrics;
16mod nns;
17mod output;
18mod replica;
19mod restore;
20mod scaffold;
21mod snapshot;
22mod status;
23mod support;
24#[cfg(test)]
25mod test_support;
26mod token;
27
28use crate::cli::{
29 clap::parse_matches,
30 globals::{
31 DISPATCH_ARGS, apply_global_icp, apply_global_network, command_local_global_option,
32 top_level_dispatch_command,
33 },
34 help::{first_arg_is_help, usage},
35};
36pub use cli::top_level_command;
37use std::ffi::OsString;
38use thiserror::Error as ThisError;
39
40const VERSION_TEXT: &str = concat!("canic ", env!("CARGO_PKG_VERSION"));
41
42#[derive(Debug, ThisError)]
47pub enum CliError {
48 #[error("{0}")]
49 Usage(String),
50
51 #[error("backup: {0}")]
52 Backup(#[from] backup::BackupCommandError),
53
54 #[error("build: {0}")]
55 Build(#[from] build::BuildCommandError),
56
57 #[error("cycles: {0}")]
58 Cycles(#[from] cycles::CyclesCommandError),
59
60 #[error("deploy: {0}")]
61 Deploy(#[from] deploy::DeployCommandError),
62
63 #[error("evidence: {0}")]
64 Evidence(#[from] evidence::EvidenceCommandError),
65
66 #[error("install: {0}")]
67 Install(#[from] install::InstallCommandError),
68
69 #[error("info: {0}")]
70 Info(#[from] info::InfoCommandError),
71
72 #[error("fleet: {0}")]
73 Fleets(#[from] fleets::FleetCommandError),
74
75 #[error("snapshot: {0}")]
76 Snapshot(#[from] snapshot::SnapshotCommandError),
77
78 #[error("scaffold: {0}")]
79 Scaffold(#[from] scaffold::ScaffoldCommandError),
80
81 #[error("restore: {0}")]
82 Restore(#[from] restore::RestoreCommandError),
83
84 #[error("replica: {0}")]
85 Replica(#[from] replica::ReplicaCommandError),
86
87 #[error("status: {0}")]
88 Status(#[from] status::StatusCommandError),
89
90 #[error("nns: {0}")]
91 Nns(#[from] nns::NnsCommandError),
92
93 #[error("token: {0}")]
94 Token(#[from] token::TokenCommandError),
95}
96
97pub fn run_from_env() -> Result<(), CliError> {
99 run(std::env::args_os().skip(1))
100}
101
102pub fn run<I>(args: I) -> Result<(), CliError>
104where
105 I: IntoIterator<Item = OsString>,
106{
107 let args = args.into_iter().collect::<Vec<_>>();
108 if first_arg_is_help(&args) {
109 println!("{}", usage());
110 return Ok(());
111 }
112 if let Some(option) = command_local_global_option(&args) {
113 return Err(CliError::Usage(format!(
114 "{option} is a top-level option; put it before the command\n\n{}",
115 usage()
116 )));
117 }
118
119 let matches =
120 parse_matches(top_level_dispatch_command(), args).map_err(|_| CliError::Usage(usage()))?;
121 if matches.get_flag("version") {
122 println!("{}", version_text());
123 return Ok(());
124 }
125 let global_icp = cli::clap::string_option(&matches, "icp");
126 let global_network = cli::clap::string_option(&matches, "network");
127
128 let Some((command, subcommand_matches)) = matches.subcommand() else {
129 return Err(CliError::Usage(usage()));
130 };
131 let mut tail = subcommand_matches
132 .get_many::<OsString>(DISPATCH_ARGS)
133 .map(|values| values.cloned().collect::<Vec<_>>())
134 .unwrap_or_default();
135 apply_global_icp(command, &mut tail, global_icp);
136 apply_global_network(command, &mut tail, global_network);
137 let tail = tail.into_iter();
138
139 match command {
140 "backup" => backup::run(tail).map_err(CliError::from),
141 "build" => build::run(tail).map_err(CliError::from),
142 "cycles" => cycles::run(tail).map_err(CliError::from),
143 "deploy" => deploy::run(tail).map_err(CliError::from),
144 "evidence" => evidence::run(tail).map_err(CliError::from),
145 "fleet" => fleets::run(tail).map_err(CliError::from),
146 "info" => info::run(tail).map_err(CliError::from),
147 "install" => install::run(tail).map_err(CliError::from),
148 "nns" => nns::run(tail).map_err(CliError::from),
149 "replica" => replica::run(tail).map_err(CliError::from),
150 "scaffold" => scaffold::run(tail).map_err(CliError::from),
151 "snapshot" => snapshot::run(tail).map_err(CliError::from),
152 "status" => status::run(tail).map_err(CliError::from),
153 "token" => token::run(tail).map_err(CliError::from),
154 "restore" => restore::run(tail).map_err(CliError::from),
155 _ => unreachable!("top-level dispatch command only defines known commands"),
156 }
157}
158
159#[must_use]
160pub const fn version_text() -> &'static str {
161 VERSION_TEXT
162}
163
164#[cfg(test)]
165mod tests;