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