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    /// Launch the interactive TUI (stub for disabled feature).
84    #[cfg(not(feature = "tui"))]
85    Tui,
86    /// Low-level access to any RomM API endpoint.
87    #[command(visible_alias = "call")]
88    Api(api::ApiCommand),
89    /// Manage gaming platforms.
90    #[command(visible_aliases = ["platform", "p", "plats"])]
91    Platforms(platforms::PlatformsCommand),
92    /// Manage ROM files and metadata.
93    #[command(visible_aliases = ["rom", "r"])]
94    Roms(Box<roms::RomsCommand>),
95    /// Trigger a library scan on the RomM server.
96    Scan(scan::ScanCommand),
97    /// Download a ROM from the server.
98    #[command(visible_aliases = ["dl", "get"])]
99    Download(download::DownloadCommand),
100    /// Manage the local persistent cache.
101    Cache(cache::CacheCommand),
102    /// Manage authentication credentials.
103    Auth(auth::AuthCommand),
104    /// Check for and install application updates.
105    Update,
106}
107
108/// Main entrypoint for running a CLI command.
109///
110/// This function initializes the [`RommClient`] and dispatches the
111/// chosen command to its respective handler.
112pub async fn run(cli: Cli, config: Config) -> Result<()> {
113    let client = RommClient::new(&config, cli.verbose)?;
114
115    match cli.command {
116        Commands::Init(_) => {
117            anyhow::bail!("internal error: init must be handled before load_config");
118        }
119        #[cfg(feature = "tui")]
120        Commands::Tui => {
121            anyhow::bail!("internal error: TUI must be started via run_interactive from main");
122        }
123        #[cfg(not(feature = "tui"))]
124        Commands::Tui => anyhow::bail!("this feature requires the tui"),
125        command => crate::frontend::cli::run(command, &client, cli.json).await?,
126    }
127
128    Ok(())
129}