ci_manager/
config.rs

1use super::*;
2use commands::Command;
3
4pub mod commands;
5
6pub static CONFIG: OnceLock<Config> = OnceLock::new();
7
8#[derive(Parser, Debug)]
9#[command(name = "CI manager")]
10#[command(bin_name = "ci-manager", version, propagate_version = true, author, styles = config_styles())]
11#[command(about = "Manage CI")]
12pub struct Config {
13    #[command(subcommand)]
14    command: Option<Command>,
15    /// Generate completion scripts for the specified shell
16    #[arg(long, global = true, value_hint = ValueHint::Other, name = "SHELL")]
17    completions: Option<clap_complete::Shell>,
18    /// Verbosity level (0-4)
19    #[arg(short, long, global = true, default_value_t = 2)]
20    verbosity: u8,
21    /// Debug flag to run through a scenario without making changes
22    #[arg(long, default_value_t = false, global = true)]
23    dry_run: bool,
24    /// Override the CI provider detection and assume the specified provider
25    #[arg(value_enum, long, global = true)]
26    ci: Option<CIProvider>,
27    /// Trim the prefix timestamp from the log output
28    #[arg(long, global = true, default_value_t = false)]
29    trim_timestamp: bool,
30    /// Trim the ansi codes from from the log output
31    #[arg(long, global = true, default_value_t = false)]
32    trim_ansi_codes: bool,
33}
34
35impl Config {
36    /// Get a reference to the global config
37    pub fn global() -> &'static Config {
38        CONFIG.get().expect("Config is not initialized")
39    }
40
41    /// Get the subcommand
42    pub fn subcmd(&self) -> &Command {
43        if let Some(subcmd) = &self.command {
44            subcmd
45        } else {
46            log::error!("Subcommand required! use `--help` for more information");
47            std::process::exit(1);
48        }
49    }
50
51    /// Generate completion scripts for the specified shell, returns true if a shell was specified
52    /// meaning the user wants to generate a completion script
53    pub fn generate_completion_script(&self) -> bool {
54        match self.completions {
55            Some(shell) => {
56                generate_completion_script(shell);
57                true
58            }
59            None => false,
60        }
61    }
62
63    /// Get the verbosity level
64    pub fn verbosity(&self) -> u8 {
65        self.verbosity
66    }
67
68    /// Get the dry run flag
69    pub fn dry_run(&self) -> bool {
70        self.dry_run
71    }
72
73    /// Get the CI provider override
74    pub fn no_ci(&self) -> Option<CIProvider> {
75        self.ci
76    }
77
78    /// Get the trim timestamp flag
79    pub fn trim_timestamp(&self) -> bool {
80        self.trim_timestamp
81    }
82
83    /// Get the trim ansi codes flag
84    pub fn trim_ansi_codes(&self) -> bool {
85        self.trim_ansi_codes
86    }
87}
88
89/// Initialize the CLI configuration
90pub fn init() -> Result<()> {
91    let config = Config::parse();
92    CONFIG.set(config).expect("Config is already initialized");
93
94    use stderrlog::LogLevelNum;
95    let log_level = match Config::global().verbosity() {
96        0 => LogLevelNum::Error,
97        1 => LogLevelNum::Warn,
98        2 => LogLevelNum::Info,
99        3 => LogLevelNum::Debug,
100        4 => LogLevelNum::Trace,
101        _ => {
102            eprintln!("Invalid verbosity level: {}", Config::global().verbosity());
103            eprintln!("Using highest verbosity level: Trace");
104            LogLevelNum::Trace
105        }
106    };
107    stderrlog::new().verbosity(log_level).quiet(false).init()?;
108
109    log::debug!("Config: {:#?}", Config::global());
110
111    if Config::global().dry_run() {
112        log::warn!("Running in dry-run mode. No writes/changes will be made");
113    }
114
115    Ok(())
116}
117
118// Styles for the help messages in the CLI
119fn config_styles() -> Styles {
120    Styles::styled()
121        .header(AnsiColor::Red.on_default() | Effects::BOLD)
122        .usage(AnsiColor::Yellow.on_default() | Effects::BOLD)
123        .literal(AnsiColor::Green.on_default() | Effects::BOLD)
124        .placeholder(AnsiColor::Blue.on_default())
125}
126
127/// Generate completion scripts for the specified shell
128fn generate_completion_script(shell: clap_complete::Shell) {
129    log::info!("Generating completion script for {shell:?}");
130    clap_complete::generate(
131        shell,
132        &mut <Config as clap::CommandFactory>::command(),
133        "ci-manager",
134        &mut std::io::stdout(),
135    );
136}