1use crate::input::load_model;
3use crate::log;
4use crate::output::{create_output_directory, get_output_dir};
5use crate::settings::Settings;
6use ::log::{error, 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: Commands,
24}
25
26#[derive(Subcommand)]
28pub enum Commands {
29 Run {
31 model_dir: PathBuf,
33 #[arg(short, long)]
35 output_dir: Option<PathBuf>,
36 },
37 Example {
39 #[command(subcommand)]
41 subcommand: ExampleSubcommands,
42 },
43}
44
45#[derive(Subcommand)]
47pub enum ExampleSubcommands {
48 List,
50 Extract {
52 name: String,
54 new_path: Option<PathBuf>,
56 },
57 Run {
59 name: String,
61 #[arg(short, long)]
63 output_dir: Option<PathBuf>,
64 },
65}
66
67pub fn handle_run_command(model_path: &Path, output_path: Option<&Path>) -> Result<()> {
69 let settings = Settings::from_path(model_path).context("Failed to load settings.")?;
71
72 let output_path = match output_path {
74 Some(p) => p.to_owned(),
75 None => get_output_dir(model_path)?,
76 };
77 create_output_directory(&output_path).context("Failed to create output directory.")?;
78
79 log::init(settings.log_level.as_deref(), &output_path)
81 .context("Failed to initialise logging.")?;
82
83 let load_and_run_model = || {
84 let (model, assets) = load_model(model_path).context("Failed to load model.")?;
86 info!("Loaded model from {}", model_path.display());
87 info!("Output data will be written to {}", output_path.display());
88
89 crate::simulation::run(model, assets, &output_path)
91 };
92
93 if let Err(err) = load_and_run_model() {
95 error!("{err:?}");
96 }
97
98 Ok(())
99}
100
101pub fn handle_example_list_command() {
103 for entry in EXAMPLES_DIR.dirs() {
104 println!("{}", entry.path().display());
105 }
106}
107
108pub fn handle_example_extract_command(name: &str, dest: Option<&Path>) -> Result<()> {
110 let dest = dest.unwrap_or(Path::new(name));
111 extract_example(name, dest)
112}
113
114fn extract_example(name: &str, new_path: &Path) -> Result<()> {
116 let sub_dir = EXAMPLES_DIR.get_dir(name).context("Example not found.")?;
118
119 ensure!(
120 !new_path.exists(),
121 "Destination directory {} already exists",
122 new_path.display()
123 );
124
125 fs::create_dir(new_path)?;
127 for entry in sub_dir.entries() {
128 match entry {
129 DirEntry::Dir(_) => panic!("Subdirectories in examples not supported"),
130 DirEntry::File(f) => {
131 let file_name = f.path().file_name().unwrap();
132 let file_path = new_path.join(file_name);
133 fs::write(&file_path, f.contents())?;
134 }
135 }
136 }
137
138 Ok(())
139}
140
141pub fn handle_example_run_command(name: &str, output_path: Option<&Path>) -> Result<()> {
143 let temp_dir = TempDir::new().context("Failed to create temporary directory.")?;
144 let model_path = temp_dir.path().join(name);
145 extract_example(name, &model_path)?;
146 handle_run_command(&model_path, output_path)
147}