1use crate::input::load_model;
3use crate::log;
4use crate::output::{create_output_directory, get_output_dir};
5use crate::settings::Settings;
6use ::log::info;
7use anyhow::{ensure, Context, Result};
8use clap::{Parser, Subcommand};
9use include_dir::{include_dir, Dir, DirEntry};
10use std::fs;
11use std::path::{Path, PathBuf};
12use tempfile::TempDir;
13
14pub const EXAMPLES_DIR: Dir = include_dir!("examples");
16
17#[derive(Parser)]
19#[command(version, about)]
20pub struct Cli {
21 #[command(subcommand)]
23 pub command: Option<Commands>,
24 #[arg(long, hide = true)]
26 pub markdown_help: bool,
27}
28
29#[derive(Subcommand)]
31pub enum Commands {
32 Run {
34 model_dir: PathBuf,
36 #[arg(short, long)]
38 output_dir: Option<PathBuf>,
39 #[arg(long)]
41 debug_model: bool,
42 },
43 Example {
45 #[command(subcommand)]
47 subcommand: ExampleSubcommands,
48 },
49}
50
51#[derive(Subcommand)]
53pub enum ExampleSubcommands {
54 List,
56 Extract {
58 name: String,
60 new_path: Option<PathBuf>,
62 },
63 Run {
65 name: String,
67 #[arg(short, long)]
69 output_dir: Option<PathBuf>,
70 #[arg(long)]
72 debug_model: bool,
73 },
74}
75
76pub fn handle_run_command(
78 model_path: &Path,
79 output_path: Option<&Path>,
80 debug_model: bool,
81) -> Result<()> {
82 let mut settings = Settings::load().context("Failed to load settings.")?;
84
85 if debug_model {
87 settings.debug_model = true;
88 }
89
90 let output_path = match output_path {
92 Some(p) => p.to_owned(),
93 None => get_output_dir(model_path)?,
94 };
95 create_output_directory(&output_path).context("Failed to create output directory.")?;
96
97 log::init(settings.log_level.as_deref(), &output_path)
99 .context("Failed to initialise logging.")?;
100
101 let (model, assets) = load_model(model_path).context("Failed to load model.")?;
103 info!("Loaded model from {}", model_path.display());
104 info!("Output data will be written to {}", output_path.display());
105
106 crate::simulation::run(model, assets, &output_path, settings.debug_model)?;
108
109 Ok(())
110}
111
112pub fn handle_example_list_command() {
114 for entry in EXAMPLES_DIR.dirs() {
115 println!("{}", entry.path().display());
116 }
117}
118
119pub fn handle_example_extract_command(name: &str, dest: Option<&Path>) -> Result<()> {
121 let dest = dest.unwrap_or(Path::new(name));
122 extract_example(name, dest)
123}
124
125fn extract_example(name: &str, new_path: &Path) -> Result<()> {
127 let sub_dir = EXAMPLES_DIR.get_dir(name).context("Example not found.")?;
129
130 ensure!(
131 !new_path.exists(),
132 "Destination directory {} already exists",
133 new_path.display()
134 );
135
136 fs::create_dir(new_path)?;
138 for entry in sub_dir.entries() {
139 match entry {
140 DirEntry::Dir(_) => panic!("Subdirectories in examples not supported"),
141 DirEntry::File(f) => {
142 let file_name = f.path().file_name().unwrap();
143 let file_path = new_path.join(file_name);
144 fs::write(&file_path, f.contents())?;
145 }
146 }
147 }
148
149 Ok(())
150}
151
152pub fn handle_example_run_command(
154 name: &str,
155 output_path: Option<&Path>,
156 debug_model: bool,
157) -> Result<()> {
158 let temp_dir = TempDir::new().context("Failed to create temporary directory.")?;
159 let model_path = temp_dir.path().join(name);
160 extract_example(name, &model_path)?;
161 handle_run_command(&model_path, output_path, debug_model)
162}