1use std::io::Write;
7
8use clap::{ArgAction, CommandFactory, Parser, Subcommand};
9use clap_complete::Shell;
10
11use crate::commands;
12use crate::context::{Ctx, GlobalArgs};
13use crate::errors::CliError;
14use crate::output::Format;
15
16#[derive(Debug, Parser)]
18#[command(
19 name = "qn",
20 version,
21 about = "Command-line interface for the Quicknode API.",
22 long_about = "qn lets you manage Quicknode endpoints, streams, webhooks, and the KV store from the terminal.\n\n\
23 Use `qn <noun> --help` (e.g. `qn endpoint --help`) for command details.\n\n\
24 Authentication is resolved in this order: --api-key flag, then the config file\n\
25 (--config-file path if given, else ~/.config/qn/config.toml). Run `qn auth login`\n\
26 to save a key the first time.",
27 propagate_version = true,
28 disable_help_subcommand = true,
29 after_help = "Examples:\n \
30 qn auth login\n \
31 qn endpoint create --chain ethereum --network mainnet\n \
32 qn endpoint list -o json\n \
33 qn endpoint logs ep-1234 --from 1h\n \
34 qn chain list",
35 next_help_heading = "Global options"
38)]
39pub struct Cli {
40 #[arg(long, global = true)]
42 pub api_key: Option<String>,
43
44 #[arg(long, global = true, value_name = "PATH")]
46 pub config_file: Option<std::path::PathBuf>,
47
48 #[arg(short = 'o', long = "format", global = true, value_enum)]
52 pub format: Option<Format>,
53
54 #[arg(long, global = true)]
56 pub no_color: bool,
57
58 #[arg(short, long, global = true)]
60 pub quiet: bool,
61
62 #[arg(short = 'w', long = "wide", global = true)]
66 pub wide: bool,
67
68 #[arg(short, long, global = true)]
70 pub verbose: bool,
71
72 #[arg(long, global = true)]
74 pub no_input: bool,
75
76 #[arg(long, global = true, default_value_t = 3, value_name = "N")]
80 pub retries: u32,
81
82 #[arg(short = 'y', long = "yes", global = true, action = ArgAction::Count)]
84 pub yes: u8,
85
86 #[arg(long, global = true, hide = true)]
89 pub base_url: Option<String>,
90
91 #[command(subcommand)]
92 pub command: Command,
93}
94
95#[derive(Debug, Subcommand)]
96pub enum Command {
97 Auth(commands::auth::Args),
99
100 #[command(visible_alias = "endpoints")]
102 Endpoint(commands::endpoint::Args),
103
104 #[command(visible_alias = "teams")]
106 Team(commands::team::Args),
107
108 Usage(commands::usage::Args),
110
111 Metrics(commands::metrics::Args),
113
114 #[command(visible_alias = "chains")]
116 Chain(commands::chain::Args),
117
118 Billing(commands::billing::Args),
120
121 #[command(visible_alias = "streams")]
123 Stream(commands::stream::Args),
124
125 #[command(visible_alias = "webhooks")]
127 Webhook(commands::webhook::Args),
128
129 Kv(commands::kv::Args),
131
132 Completions {
134 #[arg(value_enum)]
136 shell: Shell,
137 },
138}
139
140impl Cli {
141 pub fn global_args(&self) -> GlobalArgs {
143 GlobalArgs {
144 api_key: self.api_key.clone(),
145 config_file: self.config_file.clone(),
146 format: self.format,
147 wide: self.wide,
148 no_color: self.no_color,
151 quiet: self.quiet,
152 verbose: self.verbose,
153 no_input: self.no_input,
154 yes_count: self.yes,
155 retries: self.retries,
156 base_url: self.base_url.clone(),
157 }
158 }
159
160 pub async fn run(self) -> Result<(), CliError> {
166 let global = self.global_args();
167 match self.command {
168 Command::Completions { shell } => {
169 let mut cmd = <Self as CommandFactory>::command();
170 let bin_name = cmd.get_name().to_string();
171 let mut out = std::io::stdout().lock();
172 clap_complete::generate(shell, &mut cmd, bin_name, &mut out);
173 out.flush()?;
174 Ok(())
175 }
176 Command::Auth(args) => commands::auth::run(args, global).await,
177 Command::Endpoint(args) => {
178 commands::endpoint::run(args, Ctx::from_global(global)?).await
179 }
180 Command::Team(args) => commands::team::run(args, Ctx::from_global(global)?).await,
181 Command::Usage(args) => commands::usage::run(args, Ctx::from_global(global)?).await,
182 Command::Metrics(args) => commands::metrics::run(args, Ctx::from_global(global)?).await,
183 Command::Chain(args) => commands::chain::run(args, Ctx::from_global(global)?).await,
184 Command::Billing(args) => commands::billing::run(args, Ctx::from_global(global)?).await,
185 Command::Stream(args) => commands::stream::run(args, Ctx::from_global(global)?).await,
186 Command::Webhook(args) => commands::webhook::run(args, Ctx::from_global(global)?).await,
187 Command::Kv(args) => commands::kv::run(args, Ctx::from_global(global)?).await,
188 }
189 }
190}