1use clap::{Parser, Subcommand};
2use tracing::error;
3
4use crate::{
5 error::CapturedError,
6 experimental::{query::command::QueryCommand, tasks::TaskCommand},
7 invocation_context::{context, init_context},
8 proguard::ProguardSubcommand,
9 sourcemaps::{hermes::HermesSubcommand, plain::SourcemapCommand},
10};
11
12#[derive(Parser)]
13#[command(version, about, long_about = None)]
14pub struct Cli {
15 #[arg(long)]
17 host: Option<String>,
18
19 #[arg(long, default_value = "false")]
21 no_fail: bool,
22 #[arg(long, default_value = "false")]
23 skip_ssl_verification: bool,
24
25 #[command(subcommand)]
26 command: Commands,
27}
28
29#[derive(Subcommand)]
30pub enum Commands {
31 Login,
34
35 Exp {
37 #[command(subcommand)]
38 cmd: ExpCommand,
39 },
40
41 #[command(about = "Upload a directory of bundled chunks to PostHog")]
42 Sourcemap {
43 #[command(subcommand)]
44 cmd: SourcemapCommand,
45 },
46}
47
48#[derive(Subcommand)]
49pub enum ExpCommand {
50 Task {
52 #[command(subcommand)]
53 cmd: TaskCommand,
54 #[arg(long, default_value = "false")]
58 skip_ssl_verification: bool,
59 },
60
61 Query {
63 #[command(subcommand)]
64 cmd: QueryCommand,
65 },
66
67 #[command(about = "Upload hermes sourcemaps to PostHog")]
68 Hermes {
69 #[command(subcommand)]
70 cmd: HermesSubcommand,
71 },
72
73 #[command(about = "Upload proguard mapping files to PostHog")]
74 Proguard {
75 #[command(subcommand)]
76 cmd: ProguardSubcommand,
77 },
78 Schema {
80 #[command(subcommand)]
81 cmd: SchemaCommand,
82 },
83}
84
85#[derive(Subcommand)]
86pub enum SchemaCommand {
87 Pull {
89 #[arg(short, long)]
91 output: Option<String>,
92 },
93 Status,
95}
96
97impl Cli {
98 pub fn run() -> Result<(), CapturedError> {
99 let command = Cli::parse();
100 let no_fail = command.no_fail;
101
102 match command.run_impl() {
103 Ok(_) => Ok(()),
104 Err(e) => {
105 let msg = match &e.exception_id {
106 Some(id) => format!("Oops! {} (ID: {})", e.inner, id),
107 None => format!("Oops! {:?}", e.inner),
108 };
109 error!(msg);
110 if no_fail {
111 Ok(())
112 } else {
113 Err(e)
114 }
115 }
116 }
117 }
118
119 fn run_impl(self) -> Result<(), CapturedError> {
120 if !matches!(self.command, Commands::Login) {
121 init_context(self.host.clone(), self.skip_ssl_verification)?;
122 }
123
124 match self.command {
125 Commands::Login => {
126 crate::login::login(self.host)?;
128 }
129 Commands::Sourcemap { cmd } => match cmd {
130 SourcemapCommand::Inject(input_args) => {
131 crate::sourcemaps::plain::inject::inject(&input_args)?;
132 }
133 SourcemapCommand::Upload(upload_args) => {
134 crate::sourcemaps::plain::upload::upload(&upload_args)?;
135 }
136 SourcemapCommand::Process(args) => {
137 let (inject, upload) = args.into();
138 crate::sourcemaps::plain::inject::inject(&inject)?;
139 crate::sourcemaps::plain::upload::upload(&upload)?;
140 }
141 },
142 Commands::Exp { cmd } => match cmd {
143 ExpCommand::Task {
144 cmd,
145 skip_ssl_verification: _,
146 } => {
147 cmd.run()?;
148 }
149 ExpCommand::Query { cmd } => {
150 crate::experimental::query::command::query_command(&cmd)?
151 }
152 ExpCommand::Hermes { cmd } => match cmd {
153 HermesSubcommand::Inject(args) => {
154 crate::sourcemaps::hermes::inject::inject(&args)?;
155 }
156 HermesSubcommand::Upload(args) => {
157 crate::sourcemaps::hermes::upload::upload(&args)?;
158 }
159 HermesSubcommand::Clone(args) => {
160 crate::sourcemaps::hermes::clone::clone(&args)?;
161 }
162 },
163 ExpCommand::Proguard { cmd } => match cmd {
164 ProguardSubcommand::Upload(args) => {
165 crate::proguard::upload::upload(&args)?;
166 }
167 },
168 ExpCommand::Schema { cmd } => match cmd {
169 SchemaCommand::Pull { output } => {
170 crate::experimental::schema::pull(self.host, output)?;
171 }
172 SchemaCommand::Status => {
173 crate::experimental::schema::status()?;
174 }
175 },
176 },
177 }
178
179 context().finish();
180
181 Ok(())
182 }
183}