1pub(crate) mod commands;
2pub(crate) mod config;
3pub(crate) mod state;
4pub(crate) mod store;
5pub(crate) mod utils;
6
7use anyhow::Result;
8use clap::Parser;
9use commands::update::version_notice;
10use commands::{handle_command, Commands};
11use config::{ARCH, PLATFORM, VERSION};
12use state::{State, StateOptions};
13
14#[derive(Debug, Parser)]
15#[clap(
16 name = "hop",
17 about = "🐇 Interact with Hop via command line",
18 version,
19 author
20)]
21pub struct CLI {
22 #[clap(subcommand)]
23 pub commands: Commands,
24
25 #[clap(
26 short,
27 long,
28 help = "Namespace or ID of the project to use",
29 global = true
30 )]
31 pub project: Option<String>,
32
33 #[clap(short = 'D', long, help = "Enable debug mode", global = true)]
34 pub debug: bool,
35}
36
37pub async fn run() -> Result<()> {
38 let cli = CLI::parse();
40
41 utils::set_hook();
43
44 utils::logs(cli.debug);
45
46 log::debug!("Hop-CLI v{VERSION} build for {ARCH}-{PLATFORM}");
48
49 utils::sudo::fix().await?;
50
51 let state = State::new(StateOptions {
52 override_project: std::env::var("PROJECT_ID").ok().or(cli.project),
53 override_token: std::env::var("TOKEN").ok(),
54 debug: cli.debug,
55 })
56 .await?;
57
58 match cli.commands {
59 #[cfg(feature = "update")]
60 Commands::Update(_) => {}
61
62 Commands::Completions(_) => {}
65
66 _ if cfg!(debug_assertions) || state.is_ci => {}
68
69 _ => {
71 let ctx = state.ctx.clone();
72
73 tokio::spawn(async move {
74 if let Err(e) = version_notice(ctx).await {
75 log::debug!("Failed to check for updates: {e}");
76 }
77 });
78 }
79 };
80
81 if let Err(error) = handle_command(cli.commands, state).await {
82 log::error!("{error}");
83 log::debug!("{error:#?}");
84 std::process::exit(1);
85 }
86
87 utils::clean_term();
88
89 Ok(())
90}
91
92#[cfg(test)]
93mod test {
94 #[test]
95 fn test_cli() {
96 use clap::CommandFactory;
97
98 use super::*;
99
100 CLI::command().debug_assert();
101 }
102}