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/// Defines how a command should format its output for the user.
26#[derive(Clone, Copy, Debug)]
27pub enum OutputFormat {
28    /// Human-readable text format, often with tables and aligned columns.
29    Text,
30    /// Machine-friendly JSON format, useful for scripting and integration.
31    Json,
32}
33
34impl OutputFormat {
35    /// Resolves the effective output format based on global and command-specific flags.
36    ///
37    /// If either the global `--json` flag or a local `--json` flag is set,
38    /// this returns [`OutputFormat::Json`].
39    pub fn from_flags(global_json: bool, local_json: bool) -> Self {
40        if global_json || local_json {
41            OutputFormat::Json
42        } else {
43            OutputFormat::Text
44        }
45    }
46}
47
48/// Top-level CLI entrypoint for `romm-cli`.
49///
50/// This structure defines the global flags and available subcommands
51/// for the `romm-cli` binary.
52#[derive(Parser, Debug)]
53#[command(
54    name = "romm-cli",
55    version,
56    about = "Rust CLI and TUI for the ROMM API",
57    infer_subcommands = true,
58    arg_required_else_help = true
59)]
60pub struct Cli {
61    /// Increase output verbosity (logs requests to stderr).
62    #[arg(short, long, global = true)]
63    pub verbose: bool,
64
65    /// Output JSON instead of human-readable text where supported.
66    #[arg(long, global = true)]
67    pub json: bool,
68
69    /// The subcommand to execute.
70    #[command(subcommand)]
71    pub command: Commands,
72}
73
74/// All top-level commands supported by the CLI.
75#[derive(Subcommand, Debug)]
76pub enum Commands {
77    /// Create or update user configuration.
78    #[command(visible_alias = "setup")]
79    Init(init::InitCommand),
80    /// Launch the interactive Terminal User Interface (TUI).
81    #[cfg(feature = "tui")]
82    Tui {
83        /// Force show a fake update prompt for UI testing.
84        #[arg(long)]
85        mock_update: bool,
86    },
87    /// Launch the interactive TUI (stub for disabled feature).
88    #[cfg(not(feature = "tui"))]
89    Tui {
90        #[arg(long)]
91        mock_update: bool,
92    },
93    /// Low-level access to any RomM API endpoint.
94    #[command(visible_alias = "call")]
95    Api(api::ApiCommand),
96    /// Manage gaming platforms.
97    #[command(visible_aliases = ["platform", "p", "plats"])]
98    Platforms(platforms::PlatformsCommand),
99    /// Manage ROM files and metadata.
100    #[command(visible_aliases = ["rom", "r"])]
101    Roms(Box<roms::RomsCommand>),
102    /// Trigger a library scan on the RomM server.
103    Scan(scan::ScanCommand),
104    /// Download a ROM or related extras from the server.
105    #[command(visible_aliases = ["dl", "get"])]
106    Download(download::DownloadCommand),
107    /// Manage the local persistent cache.
108    Cache(cache::CacheCommand),
109    /// Manage authentication credentials.
110    Auth(auth::AuthCommand),
111    /// Check for and install application updates.
112    Update,
113}
114
115/// Main entrypoint for running a CLI command.
116///
117/// This function initializes the [`RommClient`] and dispatches the
118/// chosen command to its respective handler.
119pub async fn run(cli: Cli, config: Config) -> Result<()> {
120    let client = RommClient::new(&config, cli.verbose)?;
121
122    match cli.command {
123        Commands::Init(_) => {
124            anyhow::bail!("internal error: init must be handled before load_config");
125        }
126        #[cfg(feature = "tui")]
127        Commands::Tui { .. } => {
128            anyhow::bail!("internal error: TUI must be started via run_interactive from main");
129        }
130        #[cfg(not(feature = "tui"))]
131        Commands::Tui { .. } => anyhow::bail!("this feature requires the tui"),
132        command => crate::frontend::cli::run(command, &client, cli.json).await?,
133    }
134
135    Ok(())
136}