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