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}