1mod cache_file;
2mod cli;
3mod duration;
4mod ic_registry;
5mod nns;
6mod output;
7mod project;
8mod sns;
9pub mod subnet_catalog;
10mod table;
11
12#[cfg(test)]
13mod test_support;
14
15use crate::cli::{
16 clap::{parse_matches, string_option},
17 globals::{
18 DISPATCH_ARGS, apply_global_network, command_local_global_option,
19 top_level_dispatch_command,
20 },
21 help::{first_arg_is_help, usage},
22};
23use std::ffi::OsString;
24use thiserror::Error as ThisError;
25
26const VERSION_TEXT: &str = concat!("icq ", env!("CARGO_PKG_VERSION"));
27
28#[derive(Debug, ThisError)]
32pub enum IcQueryError {
33 #[error("{0}")]
34 Usage(String),
35
36 #[error("nns: {0}")]
37 Nns(String),
38
39 #[error("sns: {0}")]
40 Sns(String),
41}
42
43pub fn run_from_env() -> Result<(), IcQueryError> {
45 run(std::env::args_os().skip(1))
46}
47
48pub fn run<I>(args: I) -> Result<(), IcQueryError>
50where
51 I: IntoIterator<Item = OsString>,
52{
53 let args = args.into_iter().collect::<Vec<_>>();
54 if first_arg_is_help(&args) {
55 println!("{}", usage());
56 return Ok(());
57 }
58 if let Some(option) = command_local_global_option(&args) {
59 return Err(IcQueryError::Usage(format!(
60 "{option} is a top-level option; put it before the command\n\n{}",
61 usage()
62 )));
63 }
64
65 let matches = parse_matches(top_level_dispatch_command(), args)
66 .map_err(|_| IcQueryError::Usage(usage()))?;
67 if matches.get_flag("version") {
68 println!("{}", version_text());
69 return Ok(());
70 }
71 let global_network = string_option(&matches, "network");
72
73 let Some((command, subcommand_matches)) = matches.subcommand() else {
74 return Err(IcQueryError::Usage(usage()));
75 };
76 let mut tail = subcommand_matches
77 .get_many::<OsString>(DISPATCH_ARGS)
78 .map(|values| values.cloned().collect::<Vec<_>>())
79 .unwrap_or_default();
80 apply_global_network(command, &mut tail, global_network);
81 let tail = tail.into_iter();
82
83 match command {
84 "nns" => nns::run(tail).map_err(|err| IcQueryError::Nns(err.to_string())),
85 "sns" => sns::run(tail).map_err(|err| IcQueryError::Sns(err.to_string())),
86 _ => unreachable!("top-level dispatch command only defines known commands"),
87 }
88}
89
90#[must_use]
91pub const fn version_text() -> &'static str {
92 VERSION_TEXT
93}
94
95#[cfg(test)]
96mod tests;