Skip to main content

romm_cli/commands/
mod.rs

1//! Top-level CLI command handling.
2//!
3//! The `Cli` type (derived from `clap`) describes the public command-line
4//! interface. Each subcommand lives in its own module and is free to use
5//! `RommClient` directly. The TUI is just another subcommand.
6
7use anyhow::Result;
8use clap::{Parser, Subcommand};
9
10use crate::client::RommClient;
11use crate::config::Config;
12
13pub mod api;
14pub mod auth;
15pub mod cache;
16pub mod download;
17pub mod init;
18pub mod library_scan;
19pub mod platforms;
20pub mod print;
21pub mod roms;
22pub mod scan;
23pub mod update;
24
25/// How a command should format its output.
26#[derive(Clone, Copy, Debug)]
27pub enum OutputFormat {
28    /// Human-readable text (tables, aligned columns, etc.).
29    Text,
30    /// Machine-friendly JSON (pretty-printed by default).
31    Json,
32}
33
34impl OutputFormat {
35    /// Resolve the effective output format from global and per-command flags.
36    pub fn from_flags(global_json: bool, local_json: bool) -> Self {
37        if global_json || local_json {
38            OutputFormat::Json
39        } else {
40            OutputFormat::Text
41        }
42    }
43}
44
45/// Top-level CLI entrypoint for `romm-cli`.
46///
47/// This binary can be used both as:
48/// - a **TUI launcher** (`romm-cli tui`), and
49/// - a **scripting-friendly CLI** for platforms/ROMs/API calls.
50#[derive(Parser, Debug)]
51#[command(
52    name = "romm-cli",
53    version,
54    about = "Rust CLI and TUI for the ROMM API",
55    infer_subcommands = true,
56    arg_required_else_help = true
57)]
58pub struct Cli {
59    /// Increase output verbosity
60    #[arg(short, long, global = true)]
61    pub verbose: bool,
62
63    /// Output JSON instead of human-readable text where supported.
64    #[arg(long, global = true)]
65    pub json: bool,
66
67    #[command(subcommand)]
68    pub command: Commands,
69}
70
71/// All top-level commands supported by the CLI.
72#[derive(Subcommand, Debug)]
73pub enum Commands {
74    /// Create or update user config (~/.config/romm-cli/config.json or %APPDATA%\\romm-cli\\config.json)
75    #[command(visible_alias = "setup")]
76    Init(init::InitCommand),
77    /// Launch interactive TUI for exploring API endpoints
78    #[cfg(feature = "tui")]
79    Tui,
80    /// Launch interactive TUI (stub for disabled feature)
81    #[cfg(not(feature = "tui"))]
82    Tui,
83    /// Low-level access to any ROMM API endpoint
84    #[command(visible_alias = "call")]
85    Api(api::ApiCommand),
86    /// Platform-related commands
87    #[command(visible_aliases = ["platform", "p", "plats"])]
88    Platforms(platforms::PlatformsCommand),
89    /// ROM-related commands
90    #[command(visible_aliases = ["rom", "r"])]
91    Roms(Box<roms::RomsCommand>),
92    /// Trigger a full library scan on the server (`scan_library` task)
93    Scan(scan::ScanCommand),
94    /// Download a ROM
95    #[command(visible_aliases = ["dl", "get"])]
96    Download(download::DownloadCommand),
97    /// Manage the persistent ROM cache
98    Cache(cache::CacheCommand),
99    /// Manage authentication credentials stored in OS keyring/config.json
100    Auth(auth::AuthCommand),
101    /// Check for and install updates for romm-cli
102    Update,
103}
104
105pub async fn run(cli: Cli, config: Config) -> Result<()> {
106    let client = RommClient::new(&config, cli.verbose)?;
107
108    match cli.command {
109        Commands::Init(_) => {
110            anyhow::bail!("internal error: init must be handled before load_config");
111        }
112        #[cfg(feature = "tui")]
113        Commands::Tui => {
114            anyhow::bail!("internal error: TUI must be started via run_interactive from main");
115        }
116        #[cfg(not(feature = "tui"))]
117        Commands::Tui => anyhow::bail!("this feature requires the tui"),
118        command => crate::frontend::cli::run(command, &client, cli.json).await?,
119    }
120
121    Ok(())
122}