1use std::collections::BTreeMap;
4
5use anyhow::Result;
6use clap::{Parser, Subcommand};
7
8use crate::{auth, verbs};
9
10#[derive(Debug, Parser)]
11#[command(
12 name = "hm cloud",
13 about = "Talk to the Harmont cloud API",
14 disable_help_subcommand = true
15)]
16struct CloudCli {
17 #[command(subcommand)]
18 command: CloudCommand,
19}
20
21#[derive(Debug, Clone, Subcommand)]
22pub enum CloudCommand {
23 Login {
25 #[arg(long)]
27 paste: bool,
28 },
29 Logout,
31 Whoami,
33 #[command(subcommand)]
35 Org(OrgCommand),
36 #[command(subcommand)]
38 Pipeline(PipelineCommand),
39 #[command(subcommand)]
41 Build(BuildCommand),
42 #[command(subcommand)]
44 Job(JobCommand),
45 #[command(subcommand)]
47 Billing(BillingCommand),
48 Run(verbs::run::RunArgs),
50}
51
52#[derive(Debug, Clone, Subcommand)]
53pub enum OrgCommand {
54 Switch {
56 slug: String,
58 },
59}
60
61#[derive(Debug, Clone, Subcommand)]
62pub enum PipelineCommand {
63 List,
65 Show { slug: String },
67}
68
69#[derive(Debug, Clone, Subcommand)]
70pub enum BuildCommand {
71 List {
73 #[arg(short, long)]
74 pipeline: String,
75 },
76 Show {
78 #[arg(short, long)]
79 pipeline: String,
80 number: i64,
81 },
82 Cancel {
84 #[arg(short, long)]
85 pipeline: String,
86 number: i64,
87 },
88 Watch {
90 #[arg(short, long)]
91 pipeline: String,
92 number: i64,
93 },
94}
95
96#[derive(Debug, Clone, Subcommand)]
97pub enum JobCommand {
98 List {
100 #[arg(short, long)]
101 pipeline: String,
102 #[arg(short, long)]
103 build: i64,
104 },
105 Show {
107 #[arg(short, long)]
108 pipeline: String,
109 #[arg(short, long)]
110 build: i64,
111 job_id: String,
112 },
113 Log {
115 #[arg(short, long)]
116 pipeline: String,
117 #[arg(short, long)]
118 build: i64,
119 job_id: String,
120 },
121}
122
123#[derive(Debug, Clone, Subcommand)]
124pub enum BillingCommand {
125 Balance,
127 Transactions {
129 #[arg(long, default_value = "100")]
130 limit: u32,
131 },
132 Usage {
134 #[arg(long)]
135 from: Option<String>,
136 #[arg(long)]
137 to: Option<String>,
138 },
139 Topup {
141 amount_usd: u32,
142 #[arg(long)]
143 no_browser: bool,
144 },
145 Redeem { code: String },
147}
148
149pub async fn dispatch(argv: Vec<String>, env: BTreeMap<String, String>) -> Result<i32> {
152 let mut full: Vec<String> = vec!["hm cloud".to_string()];
153 full.extend(argv.into_iter().skip(1));
154 let parsed = match CloudCli::try_parse_from(&full) {
155 Ok(p) => p,
156 Err(e) => {
157 use clap::error::ErrorKind;
158 let msg = e.to_string();
159 return match e.kind() {
160 ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
161 #[allow(clippy::print_stdout)]
162 {
163 use std::io::Write;
164 std::io::stdout().write_all(msg.as_bytes()).ok();
165 }
166 Ok(0)
167 }
168 _ => {
169 #[allow(clippy::print_stderr)]
170 {
171 use std::io::Write;
172 std::io::stderr().write_all(msg.as_bytes()).ok();
173 }
174 Ok(2)
175 }
176 };
177 }
178 };
179 dispatch_command(parsed.command, env).await
180}
181
182pub async fn dispatch_command(command: CloudCommand, env: BTreeMap<String, String>) -> Result<i32> {
184 let result = match command {
185 CloudCommand::Login { paste } => auth::login::run(&env, paste).await,
186 CloudCommand::Logout => auth::logout::run(&env).await,
187 CloudCommand::Whoami => auth::whoami::run(&env).await,
188 CloudCommand::Org(cmd) => verbs::org::run(&env, cmd).await,
189 CloudCommand::Pipeline(cmd) => verbs::pipeline::run(&env, cmd).await,
190 CloudCommand::Build(cmd) => verbs::build::run(&env, cmd).await,
191 CloudCommand::Job(cmd) => verbs::job::run(&env, cmd).await,
192 CloudCommand::Billing(cmd) => verbs::billing::run(&env, cmd).await,
193 CloudCommand::Run(args) => verbs::run::run(&env, args).await,
194 };
195 match result {
196 Ok(()) => Ok(0),
197 Err(e) => {
198 tracing::error!("{e:#}");
199 Ok(1)
200 }
201 }
202}