bin_efo/
main.rs

1//! # `epoch_forge` CLI
2//!
3//! The **command‑line interface** for `epoch_forge`.  It exposes a single
4//! binary `efo` that supports the following sub‑commands:
5//!
6//! ## Sub‑modules
7//!
8//! * [`epoch`](crate::commands::epoch) - switch / advance time
9//! * [`event`](crate::commands::entity_generic) - add / remove events
10//! * [`chr`](crate::commands::entity_generic) - add / remove characters
11//! * [`town`](crate::commands::entity_generic) - add / remove towns
12//! * [`blocker`](crate::commands::entity_generic) - add / remove blockers
13//! * [`calamity`](crate::commands::entity_generic) - add / remove calamities
14//! * [`link`](crate::commands::link) - add / remove links (alliances, wars, roads, etc.)
15//!
16//! Each sub‑command is implemented in its own module under `commands/`.
17//! The `main.rs` file simply parses arguments with `clap` and dispatches
18//! to the appropriate handler.  The handlers share a common `World`
19//! instance that they load from / persist to disk.
20//!
21//! Each module exposes a `handle` function that receives the parsed arguments
22
23#![warn(missing_docs, clippy::missing_docs_in_private_items)]
24#![deny(clippy::unwrap_used)]
25
26use std::fmt::Debug;
27
28use clap::{Parser, Subcommand};
29
30use crate::{
31    commands::{
32        entity_generic::EntitySubCommands, epoch::cli_epoch::EpochCmd, generate::GenCmd,
33        init::InitCmd, show::ShowCmd,
34    },
35    error::AppError,
36};
37mod commands;
38mod error;
39mod success;
40
41/// The root command-line parser for the `efo` binary.
42///
43/// It uses `clap` to parse the top-level sub-commands (`init`, `epoch`, …)
44/// and stores them in the `command` field.
45#[derive(Parser, Debug)]
46#[command(name = "efo")]
47#[command(about = "Epoch Forge - manage organisations over time")]
48struct Cli {
49    // /// The level of verbosity
50    // #[arg(short, long, action = ArgAction::Count, global = true)]
51    // verbose: u8,
52    #[command(subcommand)]
53    /// The top level command to be executed
54    command: TopLevelCmd,
55}
56
57/// Enumeration of the top-level sub-commands that `efo` accepts.
58///
59/// Each variant corresponds to a single sub-command and holds its own
60/// argument structure.
61#[derive(Subcommand, Debug)]
62enum TopLevelCmd {
63    /// Initialise a new epoch folder.
64    Init(InitCmd),
65    /// Advance or query the current epoch.
66    #[command(subcommand)]
67    Epoch(EpochCmd),
68    /// Display some elements
69    #[command(subcommand)]
70    Show(ShowCmd),
71    /// Handle characters
72    #[command(subcommand)]
73    Chr(EntitySubCommands),
74    /// Handle events
75    #[command(subcommand)]
76    Event(EntitySubCommands),
77    // #[command(subcommand)]
78    // Org(commands::org::OrgCmd),
79    /// Handle Towns
80    #[command(subcommand)]
81    Town(EntitySubCommands),
82    /// Handle Calamities
83    #[command(subcommand)]
84    Calamity(EntitySubCommands),
85    /// Handle Blocker
86    #[command(subcommand)]
87    Blocker(EntitySubCommands),
88    //     Link(commands::link::LinkCmd),
89    /// Generate random structures and display them
90    #[command(subcommand)]
91    Gen(GenCmd),
92}
93
94/// Program entry point.
95///
96/// Parses the command-line arguments and dispatches to the selected
97/// sub-command. Errors returned by the sub-command handlers are shown
98/// in stdout and returned as a `miette::Result<()>`.
99fn main() -> miette::Result<()> {
100    env_logger::init();
101
102    let cli = Cli::parse();
103
104    if let Err(e) = exec_dispatch(cli) {
105        Err(e)?;
106    }
107
108    Ok(())
109}
110
111/// Dispatches the parsed command to the appropriate handler.
112///
113/// # Arguments
114/// * `cli` - The parsed command-line options.
115///
116/// # Returns
117/// * `Ok(())` on success.
118/// * `Err(AppError)` if the chosen sub-command handler fails.
119///
120/// The function matches on the `TopLevelCmd` variant and calls the
121/// corresponding handler in the `commands` module.  Any error is
122/// propagated to the caller, which in `main` will be shown to
123/// stdout and returned as a `miette::Result<()>`.
124fn exec_dispatch(cli: Cli) -> Result<(), AppError> {
125    match cli.command {
126        TopLevelCmd::Init(cmd) => commands::init::init(&cmd),
127        TopLevelCmd::Epoch(cmd) => commands::epoch::handle_cmd(&cmd),
128        TopLevelCmd::Show(cmd) => commands::show::handle_show_cmd(&cmd),
129        TopLevelCmd::Gen(cmd) => commands::generate::gen_handler(&cmd),
130        TopLevelCmd::Chr(cmd) => commands::entity_generic::handle_entity_commands(
131            &cmd,
132            &lib_efo::stats::EntityType::Character,
133        ),
134        TopLevelCmd::Event(cmd) => commands::entity_generic::handle_entity_commands(
135            &cmd,
136            &lib_efo::stats::EntityType::Event,
137        ),
138        TopLevelCmd::Town(cmd) => commands::entity_generic::handle_entity_commands(
139            &cmd,
140            &lib_efo::stats::EntityType::Town,
141        ),
142        TopLevelCmd::Calamity(cmd) => commands::entity_generic::handle_entity_commands(
143            &cmd,
144            &lib_efo::stats::EntityType::Monster,
145        ),
146        TopLevelCmd::Blocker(cmd) => commands::entity_generic::handle_entity_commands(
147            &cmd,
148            &lib_efo::stats::EntityType::Blocker,
149        ),
150    }
151}