1use clap::{Parser, Subcommand, ValueEnum};
2use std::path::PathBuf;
3
4use fuelcheck_core::model::OutputFormat;
5use fuelcheck_core::providers::{ProviderSelector, SourcePreference};
6use fuelcheck_core::reports::CostReportKind;
7
8use crate::logger::LogLevel;
9
10#[derive(Parser, Debug)]
11#[command(author, version, about = "Fuelcheck CLI (CodexBar-compatible)")]
12pub struct Cli {
13 #[command(flatten)]
14 pub global: GlobalArgs,
15 #[command(subcommand)]
16 pub command: Command,
17}
18
19#[derive(Parser, Debug, Clone)]
20pub struct GlobalArgs {
21 #[arg(long, global = true)]
22 pub no_color: bool,
23 #[arg(long, global = true)]
24 pub log_level: Option<LogLevel>,
25 #[arg(long, global = true)]
26 pub json_output: bool,
27 #[arg(long, global = true)]
28 pub json_only: bool,
29 #[arg(short = 'v', long, global = true)]
30 pub verbose: bool,
31}
32
33#[derive(Subcommand, Debug)]
34pub enum Command {
35 Usage(UsageArgs),
36 Cost(CostArgs),
37 Config(ConfigCommandArgs),
38 Setup(SetupArgs),
39}
40
41#[derive(Parser, Debug, Clone)]
42pub struct UsageArgs {
43 #[arg(short, long = "provider")]
44 pub providers: Vec<ProviderSelectorArg>,
45 #[arg(long, default_value = "auto")]
46 pub source: SourcePreferenceArg,
47 #[arg(long, default_value = "text")]
48 pub format: OutputFormatArg,
49 #[arg(long)]
50 pub json: bool,
51 #[arg(long)]
52 pub pretty: bool,
53 #[arg(long)]
54 pub status: bool,
55 #[arg(long)]
56 pub no_credits: bool,
57 #[arg(long)]
58 pub refresh: bool,
59 #[arg(long)]
60 pub web_debug_dump_html: bool,
61 #[arg(long, default_value = "20")]
62 pub web_timeout: u64,
63 #[arg(long)]
64 pub config: Option<PathBuf>,
65 #[arg(long)]
66 pub account: Option<String>,
67 #[arg(long)]
68 pub account_index: Option<usize>,
69 #[arg(long)]
70 pub all_accounts: bool,
71 #[arg(long)]
72 pub antigravity_plan_debug: bool,
73 #[arg(long)]
74 pub watch: bool,
75 #[arg(long, default_value = "10")]
76 pub interval: u64,
77}
78
79#[derive(Parser, Debug, Clone)]
80pub struct CostArgs {
81 #[arg(short, long = "provider")]
82 pub providers: Vec<ProviderSelectorArg>,
83 #[arg(long, default_value = "text")]
84 pub format: OutputFormatArg,
85 #[arg(long)]
86 pub json: bool,
87 #[arg(long)]
88 pub pretty: bool,
89 #[arg(long)]
90 pub report: Option<CostReportKindArg>,
91 #[arg(long)]
92 pub since: Option<String>,
93 #[arg(long)]
94 pub until: Option<String>,
95 #[arg(long)]
96 pub timezone: Option<String>,
97 #[arg(long)]
98 pub compact: bool,
99 #[arg(long)]
100 pub config: Option<PathBuf>,
101}
102
103#[derive(Parser, Debug, Clone)]
104pub struct SetupArgs {
105 #[arg(long)]
106 pub force: bool,
107 #[arg(long)]
108 pub enable_all: bool,
109 #[arg(long)]
110 pub claude_cookie: Option<String>,
111 #[arg(long)]
112 pub cursor_cookie: Option<String>,
113 #[arg(long, alias = "droid-cookie")]
114 pub factory_cookie: Option<String>,
115 #[arg(long)]
116 pub config: Option<PathBuf>,
117}
118
119#[derive(Parser, Debug)]
120pub struct ConfigCommandArgs {
121 #[command(subcommand)]
122 pub command: ConfigCommand,
123}
124
125#[derive(Subcommand, Debug)]
126pub enum ConfigCommand {
127 Validate(ConfigArgs),
128 Dump(ConfigArgs),
129}
130
131#[derive(Parser, Debug, Clone)]
132pub struct ConfigArgs {
133 #[arg(long)]
134 pub format: Option<OutputFormatArg>,
135 #[arg(long)]
136 pub pretty: bool,
137 #[arg(long)]
138 pub config: Option<PathBuf>,
139}
140
141impl ConfigCommand {
142 pub fn format(&self) -> OutputFormat {
143 match self {
144 Self::Validate(args) => args.format.map(Into::into).unwrap_or(OutputFormat::Text),
145 Self::Dump(args) => args.format.map(Into::into).unwrap_or(OutputFormat::Json),
146 }
147 }
148
149 pub fn pretty(&self) -> bool {
150 match self {
151 Self::Validate(args) | Self::Dump(args) => args.pretty,
152 }
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
157pub enum OutputFormatArg {
158 Text,
159 Json,
160}
161
162impl From<OutputFormatArg> for OutputFormat {
163 fn from(value: OutputFormatArg) -> Self {
164 match value {
165 OutputFormatArg::Text => OutputFormat::Text,
166 OutputFormatArg::Json => OutputFormat::Json,
167 }
168 }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
172pub enum SourcePreferenceArg {
173 Auto,
174 Oauth,
175 Web,
176 Cli,
177 Api,
178 Local,
179}
180
181impl From<SourcePreferenceArg> for SourcePreference {
182 fn from(value: SourcePreferenceArg) -> Self {
183 match value {
184 SourcePreferenceArg::Auto => SourcePreference::Auto,
185 SourcePreferenceArg::Oauth => SourcePreference::Oauth,
186 SourcePreferenceArg::Web => SourcePreference::Web,
187 SourcePreferenceArg::Cli => SourcePreference::Cli,
188 SourcePreferenceArg::Api => SourcePreference::Api,
189 SourcePreferenceArg::Local => SourcePreference::Local,
190 }
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
195pub enum ProviderSelectorArg {
196 Codex,
197 Claude,
198 Gemini,
199 Cursor,
200 #[value(alias = "droid")]
201 Factory,
202 Zai,
203 MiniMax,
204 Kimi,
205 #[value(alias = "kimik2")]
206 KimiK2,
207 Copilot,
208 Kiro,
209 VertexAI,
210 JetBrains,
211 Amp,
212 Warp,
213 OpenCode,
214 All,
215 Both,
216}
217
218impl From<ProviderSelectorArg> for ProviderSelector {
219 fn from(value: ProviderSelectorArg) -> Self {
220 match value {
221 ProviderSelectorArg::Codex => ProviderSelector::Codex,
222 ProviderSelectorArg::Claude => ProviderSelector::Claude,
223 ProviderSelectorArg::Gemini => ProviderSelector::Gemini,
224 ProviderSelectorArg::Cursor => ProviderSelector::Cursor,
225 ProviderSelectorArg::Factory => ProviderSelector::Factory,
226 ProviderSelectorArg::Zai => ProviderSelector::Zai,
227 ProviderSelectorArg::MiniMax => ProviderSelector::MiniMax,
228 ProviderSelectorArg::Kimi => ProviderSelector::Kimi,
229 ProviderSelectorArg::KimiK2 => ProviderSelector::KimiK2,
230 ProviderSelectorArg::Copilot => ProviderSelector::Copilot,
231 ProviderSelectorArg::Kiro => ProviderSelector::Kiro,
232 ProviderSelectorArg::VertexAI => ProviderSelector::VertexAI,
233 ProviderSelectorArg::JetBrains => ProviderSelector::JetBrains,
234 ProviderSelectorArg::Amp => ProviderSelector::Amp,
235 ProviderSelectorArg::Warp => ProviderSelector::Warp,
236 ProviderSelectorArg::OpenCode => ProviderSelector::OpenCode,
237 ProviderSelectorArg::All => ProviderSelector::All,
238 ProviderSelectorArg::Both => ProviderSelector::Both,
239 }
240 }
241}
242
243#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
244pub enum CostReportKindArg {
245 Daily,
246 Monthly,
247 Session,
248}
249
250impl From<CostReportKindArg> for CostReportKind {
251 fn from(value: CostReportKindArg) -> Self {
252 match value {
253 CostReportKindArg::Daily => CostReportKind::Daily,
254 CostReportKindArg::Monthly => CostReportKind::Monthly,
255 CostReportKindArg::Session => CostReportKind::Session,
256 }
257 }
258}