1use crate::get_muse2_config_dir;
3use crate::input::read_toml;
4use crate::log::DEFAULT_LOG_LEVEL;
5use anyhow::Result;
6use documented::DocumentedFields;
7use serde::{Deserialize, Serialize};
8use std::env;
9use std::fmt::Write;
10use std::path::{Path, PathBuf};
11
12const SETTINGS_FILE_NAME: &str = "settings.toml";
13
14const DEFAULT_SETTINGS_FILE_HEADER: &str = concat!(
15 "# This file contains the program settings for MUSE2.
16#
17# The default options for MUSE2 v",
18 env!("CARGO_PKG_VERSION"),
19 " are shown below, commented out. To change an option, uncomment it and set the value
20# appropriately.
21#
22# To show the default options for the current version of MUSE2, run:
23# \tmuse2 settings show-default
24#
25# For information about the possible settings, visit:
26# \t",
27 crate::docs_url!("file_formats/program_settings.html"),
28 "\n"
29);
30
31pub fn get_settings_file_path() -> PathBuf {
33 let mut path = get_muse2_config_dir();
34 path.push(SETTINGS_FILE_NAME);
35
36 path
37}
38
39#[derive(Debug, DocumentedFields, Serialize, Deserialize, PartialEq)]
44#[serde(default)]
45pub struct Settings {
46 pub log_level: String,
48 pub overwrite: bool,
50 pub debug_model: bool,
52 pub results_root: PathBuf,
54 pub graph_results_root: PathBuf,
56 pub copy_input_files: bool,
58}
59
60impl Default for Settings {
61 fn default() -> Self {
62 Self {
63 log_level: DEFAULT_LOG_LEVEL.to_string(),
64 overwrite: false,
65 debug_model: false,
66 results_root: PathBuf::from("muse2_results"),
67 graph_results_root: PathBuf::from("muse2_graphs"),
68 copy_input_files: true,
69 }
70 }
71}
72
73impl Settings {
74 pub fn load_or_default() -> Result<Settings> {
83 if env::var("MUSE2_USE_DEFAULT_SETTINGS").is_ok_and(|v| v == "1") {
84 Ok(Settings::default())
85 } else {
86 Self::from_path_or_default(&get_settings_file_path())
87 }
88 }
89
90 fn from_path_or_default(file_path: &Path) -> Result<Settings> {
93 if !file_path.is_file() {
94 return Ok(Settings::default());
95 }
96
97 read_toml(file_path)
98 }
99
100 pub fn default_file_contents() -> String {
102 let settings = Settings::default();
104
105 let settings_raw = toml::to_string(&settings).expect("Could not convert settings to TOML");
107
108 let mut out = DEFAULT_SETTINGS_FILE_HEADER.to_string();
111 for line in settings_raw.split('\n') {
112 if let Some((field, _)) = line.split_once('=') {
113 let field = field.trim();
115
116 let docs = Settings::get_field_docs(field).expect("Missing doc comment for field");
118 for line in docs.split('\n') {
119 write!(&mut out, "\n# # {}\n", line.trim()).unwrap();
120 }
121
122 writeln!(&mut out, "# {}", line.trim()).unwrap();
123 }
124 }
125
126 out
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use std::fs::File;
134 use std::io::Write;
135 use tempfile::tempdir;
136
137 #[test]
138 fn settings_from_path_or_default_no_file() {
139 let dir = tempdir().unwrap();
140 let file_path = dir.path().join(SETTINGS_FILE_NAME); assert_eq!(
142 Settings::from_path_or_default(&file_path).unwrap(),
143 Settings::default()
144 );
145 }
146
147 #[test]
148 fn settings_from_path_or_default() {
149 let dir = tempdir().unwrap();
150 let file_path = dir.path().join(SETTINGS_FILE_NAME);
151
152 {
153 let mut file = File::create(&file_path).unwrap();
154 writeln!(file, "log_level = \"warn\"").unwrap();
155 }
156
157 assert_eq!(
158 Settings::from_path_or_default(&file_path).unwrap(),
159 Settings {
160 log_level: "warn".to_string(),
161 ..Settings::default()
162 }
163 );
164 }
165
166 #[test]
167 fn default_file_contents() {
168 assert!(!Settings::default_file_contents().is_empty());
169 }
170}