1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
pub(crate) mod commands;
pub(crate) mod config;
pub(crate) mod state;
pub(crate) mod store;
pub(crate) mod utils;

use anyhow::Result;
use clap::Parser;

use commands::update::version_notice;
#[cfg(feature = "update")]
use commands::Commands::Update;
use commands::{handle_command, Commands};
use config::{ARCH, PLATFORM, VERSION};
use state::{State, StateOptions};

#[derive(Debug, Parser)]
#[clap(
    name = "hop",
    about = "🐇 Interact with Hop via command line",
    version,
    author
)]
pub struct CLI {
    #[clap(subcommand)]
    pub commands: Commands,

    #[clap(
        short,
        long,
        help = "Namespace or ID of the project to use",
        global = true
    )]
    pub project: Option<String>,

    #[clap(short = 'D', long, help = "Enable debug mode", global = true)]
    pub debug: bool,
}

pub async fn run() -> Result<()> {
    // create a new CLI instance
    let cli = CLI::parse();

    // setup panic hook
    utils::set_hook();

    utils::logs(cli.debug);

    // in the debug mode, print the version and arch for easier debugging
    log::debug!("Hop-CLI v{VERSION} build for {ARCH}-{PLATFORM}");

    utils::sudo::fix().await?;

    let state = State::new(StateOptions {
        override_project: std::env::var("PROJECT_ID").ok().or(cli.project),
        override_token: std::env::var("TOKEN").ok(),
    })
    .await?;

    match cli.commands {
        #[cfg(feature = "update")]
        Update(_) => None,

        // only show the notice if we are not in debug mode or in CI
        _ if cfg!(debug_assertions) || state.is_ci => None,

        // its okay for the notice to fail
        _ => version_notice(state.ctx.clone()).await.ok(),
    };

    if let Err(error) = handle_command(cli.commands, state).await {
        log::error!("{error}");
        std::process::exit(1);
    }

    utils::clean_term();

    Ok(())
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_cli() {
        use clap::CommandFactory;

        use super::*;

        CLI::command().debug_assert();
    }
}