gitee-cli-rs 0.2.2

A gh-like command-line client for Gitee
Documentation
use std::ffi::OsString;
use std::io::IsTerminal;

use clap::Parser;
use gitee_cli_rs::config::{self, Config};
use gitee_cli_rs::error::GiteeError;
use gitee_cli_rs::update_notice::{self, UpdateCheckGates};

fn main() {
    let settings = Config::load_settings().unwrap_or_else(|_| Default::default());
    let args_os: Vec<OsString> = std::env::args_os().collect();
    let debug = args_os.iter().any(|a| a == "--debug");

    let cli = if let Some(raw) = args_to_utf8(&args_os) {
        let with_defaults = config::apply_defaults(raw.clone(), &settings);
        match config::expand_aliases(with_defaults.clone(), &settings.aliases) {
            Ok(expanded) => {
                if debug && expanded != with_defaults {
                    eprintln!("alias expanded: {}", expanded.join(" "));
                }
                gitee_cli_rs::cli::Cli::parse_from(expanded)
            }
            Err(e) => {
                eprintln!("error: {e}");
                std::process::exit(2);
            }
        }
    } else {
        let with_defaults = config::apply_defaults_os(args_os, &settings);
        gitee_cli_rs::cli::Cli::parse_from(with_defaults)
    };

    let json = cli.json.is_some();
    let debug = cli.debug;

    // Start Update notice check before command work when skip gates allow.
    let gates = UpdateCheckGates {
        json,
        stdout_is_tty: std::io::stdout().is_terminal(),
        stderr_is_tty: std::io::stderr().is_terminal(),
    };
    let update_notice = update_notice::maybe_spawn(
        env!("CARGO_PKG_VERSION"),
        update_notice::GITHUB_API_BASE,
        &gates,
        &settings,
    );

    match gitee_cli_rs::cmd::run(cli) {
        Ok(()) => {
            if let Some(notice) = update_notice {
                let mut stderr = std::io::stderr().lock();
                notice.finish_on_success(&mut stderr);
            }
        }
        Err(e) => {
            drop(update_notice);
            print_error(&e, json, debug);
            std::process::exit(e.exit_code());
        }
    }
}

/// Print an error to stderr. When `--json` is in effect, emit a structured
/// JSON envelope; otherwise a human-readable `error: …` line.
fn print_error(e: &GiteeError, json: bool, debug: bool) {
    if json {
        let envelope = serde_json::json!({
            "code": e.code_slug(),
            "message": e.to_string(),
            "exit_code": e.exit_code(),
        });
        eprintln!("{}", serde_json::to_string(&envelope).unwrap_or_else(|_| format!("{{\"code\":\"error\",\"message\":\"{}\"}}", e)));
    } else if debug {
        eprintln!("error: {e:?}");
    } else {
        eprintln!("error: {e}");
    }
}

fn args_to_utf8(args: &[OsString]) -> Option<Vec<String>> {
    args.iter()
        .map(|a| a.clone().into_string().ok())
        .collect()
}