gh_workflow_parser/
config.rs

1//! CLI configuration and initialization
2use crate::gh::gh_cli;
3use crate::util::check_gh_cli_version;
4
5use super::commands::Command;
6use clap::builder::styling::{AnsiColor, Effects, Styles};
7use clap::*;
8use std::error::Error;
9use which::which;
10
11/// The minimum version of the GitHub CLI required for `gh-workflow-parser` to run as expected.
12pub const GH_CLI_MIN_VERSION: semver::Version = semver::Version::new(2, 43, 1);
13
14#[derive(Parser, Debug)]
15#[command(version, about = "Parse GitHub CI workflows", author, styles = config_styles())]
16pub struct Config {
17    #[command(subcommand)]
18    command: Option<Command>,
19    /// Debug flag to run through a scenario without making changes
20    #[arg(long, default_value_t = false, global = true)]
21    dry_run: bool,
22    /// Fake the GitHub CLI for testing
23    #[arg(long, default_value_t = false, global = true)]
24    fake_github_cli: bool,
25    /// Verbosity level (0-4)
26    #[arg(short, long, global = true, default_value_t = 2)]
27    verbosity: u8,
28    /// Generate completion scripts for the specified shell
29    #[arg(long, global = true, value_hint = ValueHint::Other, name = "SHELL")]
30    completions: Option<clap_complete::Shell>,
31}
32
33impl Config {
34    /// Get the dry run flag
35    pub fn dry_run(&self) -> bool {
36        self.dry_run
37    }
38
39    /// Get the fake GitHub CLI flag
40    pub fn fake_github_cli(&self) -> bool {
41        self.fake_github_cli
42    }
43
44    /// Get the subcommand
45    pub fn subcmd(&self) -> &Command {
46        if self.command.is_none() {
47            log::error!("Subcommand required! use `--help` for more information");
48            std::process::exit(1);
49        }
50        self.command.as_ref().expect("Subcommand not set")
51    }
52
53    /// Get the verbosity level
54    pub fn verbosity(&self) -> u8 {
55        self.verbosity
56    }
57
58    pub fn generate_completion_script(&self) -> bool {
59        match self.completions {
60            Some(shell) => {
61                generate_completion_script(shell);
62                true
63            },
64            None => false,
65        }
66    }
67}
68
69/// Initialize the CLI configuration
70pub fn init() -> Result<Config, Box<dyn Error>> {
71    let config = Config::parse();
72    use stderrlog::LogLevelNum;
73    let log_level = match config.verbosity() {
74        0 => LogLevelNum::Error,
75        1 => LogLevelNum::Warn,
76        2 => LogLevelNum::Info,
77        3 => LogLevelNum::Debug,
78        4 => LogLevelNum::Trace,
79        _ => {
80            eprintln!("Invalid verbosity level: {}", config.verbosity());
81            eprintln!("Using highest verbosity level: Trace");
82            LogLevelNum::Trace
83        },
84    };
85    stderrlog::new().verbosity(log_level).quiet(false).init()?;
86    if config.dry_run() {
87        log::warn!("Running in dry-run mode. No writes/changes will be made");
88    }
89
90    // Check that the GitHub CLI is installed
91    if let Err(e) = which(gh_cli()) {
92        log::error!("GitHub CLI not found: {e}");
93        std::process::exit(1);
94    }
95    check_gh_cli_version(GH_CLI_MIN_VERSION)?;
96
97    Ok(config)
98}
99
100// Styles for the help messages in the CLI
101fn config_styles() -> Styles {
102    Styles::styled()
103        .header(AnsiColor::Red.on_default() | Effects::BOLD)
104        .usage(AnsiColor::Yellow.on_default() | Effects::BOLD)
105        .literal(AnsiColor::Green.on_default() | Effects::BOLD)
106        .placeholder(AnsiColor::Blue.on_default())
107}
108
109/// Generate completion scripts for the specified shell
110fn generate_completion_script(shell: clap_complete::Shell) {
111    log::info!("Generating completion script for {shell:?}");
112    clap_complete::generate(
113        shell,
114        &mut <Config as clap::CommandFactory>::command(),
115        "gh-workflow-parser",
116        &mut std::io::stdout(),
117    );
118}