Skip to main content

git_same/commands/
mod.rs

1//! Command handlers for the CLI subcommands.
2//!
3//! This module contains the runtime behavior for each subcommand,
4//! separated from `main.rs` so the entrypoint stays focused on bootstrapping.
5
6pub mod init;
7pub mod reset;
8pub mod scan;
9#[cfg(feature = "tui")]
10pub mod setup;
11pub mod status;
12pub mod support;
13pub mod sync_cmd;
14pub mod workspace;
15
16pub use init::run as run_init;
17pub use status::run as run_status;
18pub use sync_cmd::run as run_sync_cmd;
19
20use crate::cli::Command;
21use crate::config::Config;
22use crate::errors::{AppError, Result};
23use crate::output::Output;
24use std::path::Path;
25
26pub(crate) use support::{ensure_base_path, warn_if_concurrency_capped};
27
28/// Run the specified command.
29pub async fn run_command(
30    command: &Command,
31    config_path: Option<&Path>,
32    output: &Output,
33    quiet: bool,
34) -> Result<()> {
35    // Print the canonical CLI banner for all subcommands.
36    if !output.is_json() && !quiet {
37        crate::banner::print_banner();
38    }
39
40    // Commands that don't need config
41    if let Command::Init(args) = command {
42        return run_init(args, output).await;
43    }
44    if let Command::Reset(args) = command {
45        return reset::run(args, output).await;
46    }
47    if let Command::Scan(args) = command {
48        return scan::run(args, config_path, output);
49    }
50    #[cfg(feature = "tui")]
51    if let Command::Setup(args) = command {
52        return setup::run(args, output).await;
53    }
54
55    // Load config for all other commands
56    let config = load_config(config_path)?;
57
58    match command {
59        Command::Init(_) | Command::Reset(_) | Command::Scan(_) => unreachable!(),
60        #[cfg(feature = "tui")]
61        Command::Setup(_) => unreachable!(),
62        Command::Sync(args) => run_sync_cmd(args, &config, output).await,
63        Command::Status(args) => run_status(args, &config, output).await,
64        Command::Workspace(args) => workspace::run(args, &config, output),
65    }
66}
67
68/// Load configuration from the given path or default location.
69///
70/// Returns an error suggesting `gisa init` when no config file exists
71/// at the default location, rather than silently using defaults.
72fn load_config(config_path: Option<&Path>) -> Result<Config> {
73    let path = match config_path {
74        Some(p) => p.to_path_buf(),
75        None => Config::default_path()?,
76    };
77    if !path.exists() {
78        return Err(AppError::config(
79            "No configuration found. Run 'gisa init' to create one.",
80        ));
81    }
82    Config::load_from(&path)
83}