1use crate::graph::save_commodity_graphs_for_model;
3use crate::input::{load_commodity_graphs, load_model};
4use crate::log;
5use crate::output::{create_output_directory, get_graphs_dir, get_output_dir};
6use crate::settings::Settings;
7use ::log::{info, warn};
8use anyhow::{Context, Result};
9use clap::{Args, CommandFactory, Parser, Subcommand};
10use std::path::{Path, PathBuf};
11
12pub mod example;
13use example::ExampleSubcommands;
14
15pub mod settings;
16use settings::SettingsSubcommands;
17
18#[derive(Parser)]
20#[command(version, about)]
21struct Cli {
22 #[command(subcommand)]
24 command: Option<Commands>,
25 #[arg(long, hide = true)]
27 markdown_help: bool,
28}
29
30#[derive(Args)]
32pub struct RunOpts {
33 #[arg(short, long)]
35 pub output_dir: Option<PathBuf>,
36 #[arg(long)]
38 pub overwrite: bool,
39 #[arg(long)]
41 pub debug_model: bool,
42}
43
44#[derive(Args)]
46pub struct GraphOpts {
47 #[arg(short, long)]
49 pub output_dir: Option<PathBuf>,
50 #[arg(long)]
52 pub overwrite: bool,
53}
54
55#[derive(Subcommand)]
57enum Commands {
58 Run {
60 model_dir: PathBuf,
62 #[command(flatten)]
64 opts: RunOpts,
65 },
66 Example {
68 #[command(subcommand)]
70 subcommand: ExampleSubcommands,
71 },
72 Validate {
74 model_dir: PathBuf,
76 },
77 SaveGraphs {
79 model_dir: PathBuf,
81 #[command(flatten)]
83 opts: GraphOpts,
84 },
85 Settings {
87 #[command(subcommand)]
89 subcommand: SettingsSubcommands,
90 },
91}
92
93impl Commands {
94 fn execute(self) -> Result<()> {
96 match self {
97 Self::Run { model_dir, opts } => handle_run_command(&model_dir, &opts),
98 Self::Example { subcommand } => subcommand.execute(),
99 Self::Validate { model_dir } => handle_validate_command(&model_dir),
100 Self::SaveGraphs { model_dir, opts } => handle_save_graphs_command(&model_dir, &opts),
101 Self::Settings { subcommand } => subcommand.execute(),
102 }
103 }
104}
105
106pub fn run_cli() -> Result<()> {
108 let cli = Cli::parse();
109
110 if cli.markdown_help {
112 clap_markdown::print_help_markdown::<Cli>();
113 return Ok(());
114 }
115
116 if let Some(command) = cli.command {
117 command.execute()?;
118 } else {
119 Cli::command().print_long_help()?;
121 }
122
123 Ok(())
124}
125
126pub fn handle_run_command(model_path: &Path, opts: &RunOpts) -> Result<()> {
128 let mut settings = Settings::load_or_default().context("Failed to load settings.")?;
129
130 if opts.debug_model {
132 settings.debug_model = true;
133 }
134 if opts.overwrite {
135 settings.overwrite = true;
136 }
137
138 let pathbuf: PathBuf;
140 let output_path = if let Some(p) = opts.output_dir.as_deref() {
141 p
142 } else {
143 pathbuf = get_output_dir(model_path, settings.results_root)?;
144 &pathbuf
145 };
146
147 let overwrite =
148 create_output_directory(output_path, settings.overwrite).with_context(|| {
149 format!(
150 "Failed to create output directory: {}",
151 output_path.display()
152 )
153 })?;
154
155 log::init(&settings.log_level, Some(output_path)).context("Failed to initialise logging.")?;
157
158 info!("Starting MUSE2 v{}", env!("CARGO_PKG_VERSION"));
159
160 let (model, assets) = load_model(model_path).context("Failed to load model.")?;
162 info!("Loaded model from {}", model_path.display());
163 info!("Output folder: {}", output_path.display());
164
165 if overwrite {
167 warn!("Output folder will be overwritten");
168 }
169
170 crate::simulation::run(&model, assets, output_path, settings.debug_model)?;
172 info!("Simulation complete!");
173
174 Ok(())
175}
176
177pub fn handle_validate_command(model_path: &Path) -> Result<()> {
179 let settings = Settings::load_or_default().context("Failed to load settings.")?;
180
181 log::init(&settings.log_level, None).context("Failed to initialise logging.")?;
183
184 load_model(model_path).context("Failed to validate model.")?;
186 info!("Model validation successful!");
187
188 Ok(())
189}
190
191pub fn handle_save_graphs_command(model_path: &Path, opts: &GraphOpts) -> Result<()> {
193 let mut settings = Settings::load_or_default().context("Failed to load settings.")?;
194
195 if opts.overwrite {
196 settings.overwrite = true;
197 }
198
199 let pathbuf: PathBuf;
201 let output_path = if let Some(p) = opts.output_dir.as_deref() {
202 p
203 } else {
204 pathbuf = get_graphs_dir(model_path, settings.graph_results_root)?;
205 &pathbuf
206 };
207
208 let overwrite =
209 create_output_directory(output_path, settings.overwrite).with_context(|| {
210 format!(
211 "Failed to create graphs directory: {}",
212 output_path.display()
213 )
214 })?;
215
216 log::init(&settings.log_level, None).context("Failed to initialise logging.")?;
218
219 if overwrite {
221 warn!("Graphs directory will be overwritten");
222 }
223
224 let commodity_graphs = load_commodity_graphs(model_path).context("Failed to build graphs.")?;
226 save_commodity_graphs_for_model(&commodity_graphs, output_path)?;
227 info!("Graphs saved to: {}", output_path.display());
228
229 Ok(())
230}