diff --git a/crates/desktop/artcraft/src/commands/app_preferences/get_app_preferences_command.rs b/crates/desktop/artcraft/src/commands/app_preferences/get_app_preferences_command.rs new file mode 100644 index 0000000000..6413445c35 --- /dev/null +++ b/crates/desktop/artcraft/src/commands/app_preferences/get_app_preferences_command.rs @@ -0,0 +1,46 @@ +use crate::state::app_preferences::app_preferences_manager::AppPreferencesManager; +use crate::state::app_preferences::preferred_download_directory::PreferredDownloadDirectory; +use errors::AnyhowResult; +use log::{error, info}; +use serde_derive::Serialize; +use tauri::{AppHandle, State}; + +#[derive(Serialize)] +pub struct GetAppPreferencesResponse { + pub preferences: AppPreferencesPayload, +} + +#[derive(Serialize)] +pub struct AppPreferencesPayload { + /// The downloads directory to use when a user downloads a file. + pub preferred_download_directory: PreferredDownloadDirectory, + /// Play sounds on events. + pub play_sounds: bool, +} + + +#[tauri::command] +pub async fn get_app_preferences_command( + app_prefs: State<'_, AppPreferencesManager>, +) -> Result { + info!("get_app_preferences_command called"); + + let result = get_prefs(&app_prefs) + .await + .map_err(|err| { + error!("Error getting app preferences: {:?}", err); + format!("Error getting app preferences: {:?}", err) + })?; + + Ok(GetAppPreferencesResponse { + preferences: result, + }) +} + +async fn get_prefs(app_prefs: &AppPreferencesManager) -> AnyhowResult { + let prefs = app_prefs.get_clone()?; + Ok(AppPreferencesPayload { + preferred_download_directory: prefs.preferred_download_directory, + play_sounds: prefs.play_sounds, + }) +} diff --git a/crates/desktop/artcraft/src/commands/app_preferences/mod.rs b/crates/desktop/artcraft/src/commands/app_preferences/mod.rs new file mode 100644 index 0000000000..d032ba902b --- /dev/null +++ b/crates/desktop/artcraft/src/commands/app_preferences/mod.rs @@ -0,0 +1,2 @@ +pub mod get_app_preferences_command; +pub mod update_app_preference_command; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/commands/app_preferences/update_app_preference_command.rs b/crates/desktop/artcraft/src/commands/app_preferences/update_app_preference_command.rs new file mode 100644 index 0000000000..8d15b39c3c --- /dev/null +++ b/crates/desktop/artcraft/src/commands/app_preferences/update_app_preference_command.rs @@ -0,0 +1,70 @@ +use crate::state::app_preferences::app_preferences_manager::AppPreferencesManager; +use crate::state::app_preferences::preferred_download_directory::PreferredDownloadDirectory; +use crate::state::data_dir::app_data_root::AppDataRoot; +use errors::AnyhowResult; +use log::{error, info}; +use serde_derive::{Deserialize, Serialize}; +use tauri::{AppHandle, State}; + +/// For now, we'll only update a single preference at a time. +#[derive(Deserialize)] +pub struct UpdateAppPreferencesRequest { + pub preference: PreferenceName, + /// We'll decode this with respect to the preference value. + pub value: String, +} + +#[derive(Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum PreferenceName { + PreferredDownloadDirectory, + PlaySounds, +} + +#[derive(Serialize)] +pub struct UpdateAppPreferencesResponse { + pub success: bool, +} + + +#[tauri::command] +pub async fn update_app_preferences_command( + request: UpdateAppPreferencesRequest, + app_prefs: State<'_, AppPreferencesManager>, + app_data_root: State<'_, AppDataRoot>, +) -> Result { + info!("update_app_preferences_command called"); + + update_prefs(request, &app_prefs, &app_data_root) + .await + .map_err(|err| { + error!("Error getting app preferences: {:?}", err); + format!("Error getting app preferences: {:?}", err) + })?; + + Ok(UpdateAppPreferencesResponse { + success: true, + }) +} + +async fn update_prefs( + request: UpdateAppPreferencesRequest, + app_prefs: &AppPreferencesManager, + app_data_root: &AppDataRoot, +) -> AnyhowResult<()> { + let mut prefs = app_prefs.get_clone()?; + + match request.preference { + PreferenceName::PreferredDownloadDirectory => { + prefs.preferred_download_directory = request.value.parse::()?; + } + PreferenceName::PlaySounds => { + prefs.play_sounds = request.value.parse::()?; + } + } + + app_prefs.set_clone(&prefs)?; + app_data_root.settings_dir().write_app_preferences(&prefs)?; + + Ok(()) +} diff --git a/crates/desktop/artcraft/src/commands/mod.rs b/crates/desktop/artcraft/src/commands/mod.rs index b0a13792e6..a83adf2f50 100644 --- a/crates/desktop/artcraft/src/commands/mod.rs +++ b/crates/desktop/artcraft/src/commands/mod.rs @@ -1,3 +1,4 @@ +pub mod app_preferences; pub mod flip_image; pub mod platform_info_command; pub mod sora; diff --git a/crates/desktop/artcraft/src/lib.rs b/crates/desktop/artcraft/src/lib.rs index 5017b8769e..03e91f17fa 100644 --- a/crates/desktop/artcraft/src/lib.rs +++ b/crates/desktop/artcraft/src/lib.rs @@ -11,17 +11,19 @@ use crate::commands::platform_info_command::platform_info_command; use crate::commands::sora::open_sora_login_command::open_sora_login_command; use crate::commands::sora::sora_image_generation_command::sora_image_generation_command; use crate::commands::sora::sora_image_remix_command::sora_image_remix_command; -use crate::state::app_config::AppConfig; +use crate::commands::app_preferences::get_app_preferences_command::get_app_preferences_command; +use crate::state::data_dir::app_data_root::AppDataRoot; use crate::state::main_window_size::MainWindowSize; use crate::state::sora::sora_credential_manager::SoraCredentialManager; -use crate::utils::webview_unsafe::webview_unsafe_for_app; use crate::state::sora::sora_task_queue::SoraTaskQueue; use crate::state::storyteller::storyteller_credential_manager::StorytellerCredentialManager; use crate::threads::discord_presence_thread::discord_presence_thread; use crate::threads::main_window_thread::main_window_thread::main_window_thread; use crate::threads::sora_session_login_thread::sora_session_login_thread; use crate::threads::sora_task_polling_thread::sora_task_polling_thread; +use crate::utils::webview_unsafe::webview_unsafe_for_app; +use crate::state::app_preferences::app_preferences_manager::load_app_preferences_or_default; use tauri_plugin_http; use tauri_plugin_log::Target; use tauri_plugin_log::TargetKind; @@ -30,14 +32,13 @@ use tauri_plugin_log::TargetKind; pub fn run() { // NB: Tauri wants to install the logger itself, so we can't rely on the logger crate // until the tauri runtime begins. - println!("Loading model config..."); - - let config = AppConfig::init() - .expect("config should load"); - - let app_data_root = config.app_data_root.clone(); - let app_data_root_2 = config.app_data_root.clone(); + println!("Loading config..."); + let app_data_root = AppDataRoot::create_default().expect("data directory should be created"); + let app_data_root_2 = app_data_root.clone(); + println!("Loading app preferences..."); + let app_preferences = load_app_preferences_or_default(&app_data_root); + println!("Attempting to read existing artcraft credentials..."); let storyteller_creds_manager = StorytellerCredentialManager::initialize_from_disk_infallible(&app_data_root); let storyteller_creds_manager_2 = storyteller_creds_manager.clone(); @@ -112,11 +113,12 @@ pub fn run() { Ok(()) }) .manage(app_data_root) - .manage(config) + .manage(app_preferences) .manage(sora_creds_manager) .manage(sora_task_queue) .invoke_handler(tauri::generate_handler![ flip_image, + get_app_preferences_command, open_sora_login_command, platform_info_command, sora_image_generation_command, diff --git a/crates/desktop/artcraft/src/state/app_config.rs b/crates/desktop/artcraft/src/state/app_config.rs deleted file mode 100644 index a090a53d82..0000000000 --- a/crates/desktop/artcraft/src/state/app_config.rs +++ /dev/null @@ -1,58 +0,0 @@ -use crate::state::data_dir::app_data_root::AppDataRoot; -use crate::state::os_platform::OsPlatform; -use crate::state::yaml_config::YamlConfig; - -const DEFAULT_SD_IMAGE_WIDTH: usize = 1024; -const DEFAULT_SD_IMAGE_HEIGHT: usize = 1024; - - -/// A central place to configure app-wide details. -pub struct AppConfig { - pub image_height: usize, - pub image_width: usize, - pub scheduler_steps: usize, - pub scheduler_samples: usize, - - pub seed: Option, - - pub cfg_scale: Option, - - /// Root location for application data. - pub app_data_root: AppDataRoot, -} - -impl AppConfig { - - pub fn init() -> anyhow::Result { - let yaml_configs = YamlConfig::load_from_config_file_recursive(); - - println!("Configs: {:?}", yaml_configs); - - let os_platform = OsPlatform::get(); - - let default_data_root = match os_platform { - OsPlatform::Linux => yaml_configs.linux_default_data_path.as_deref(), - OsPlatform::MacOs => yaml_configs.mac_default_data_path.as_deref(), - OsPlatform::Windows => yaml_configs.windows_default_data_path.as_deref(), - }; - - println!("Possible user-defined data root: {:?}", default_data_root); - - let app_data_root = match default_data_root { - None => AppDataRoot::create_default()?, - Some(path) => AppDataRoot::create_existing(path)?, - }; - - println!("App data root: {:?}", app_data_root.path()); - - Ok(Self { - image_height: yaml_configs.image_height.unwrap_or(DEFAULT_SD_IMAGE_HEIGHT), - image_width: yaml_configs.image_width.unwrap_or(DEFAULT_SD_IMAGE_WIDTH), - scheduler_steps: yaml_configs.scheduler_steps.unwrap_or(4), - scheduler_samples: yaml_configs.scheduler_samples.unwrap_or(15), - seed: yaml_configs.seed, - cfg_scale: yaml_configs.cfg_scale, - app_data_root, - }) - } -} diff --git a/crates/desktop/artcraft/src/state/app_preferences/app_preferences.rs b/crates/desktop/artcraft/src/state/app_preferences/app_preferences.rs new file mode 100644 index 0000000000..2a5b6b7118 --- /dev/null +++ b/crates/desktop/artcraft/src/state/app_preferences/app_preferences.rs @@ -0,0 +1,50 @@ +use crate::state::app_preferences::app_preferences_serializable::AppPreferencesSerializable; +use crate::state::app_preferences::preferred_download_directory::PreferredDownloadDirectory; +use crate::state::data_dir::app_data_root::AppDataRoot; + +#[derive(Clone)] +pub struct AppPreferences { + /// The downloads directory to use when a user downloads a file. + pub preferred_download_directory: PreferredDownloadDirectory, + + /// Play sounds on events. + pub play_sounds: bool, +} + +impl Default for AppPreferences { + fn default() -> Self { + Self { + preferred_download_directory: PreferredDownloadDirectory::SystemDefault, + play_sounds: true, + } + } +} + +impl AppPreferences { + pub fn load_from_file_or_default(data_root: &AppDataRoot) -> Self { + let filename = data_root.settings_dir().get_app_preferences_path(); + if !filename.exists() { + return Self::default(); + } + + match AppPreferencesSerializable::load_from_file(data_root) { + Ok(Some(serializable)) => { + serializable.to_preferences() + } + Ok(None) => { + Self::default() + } + Err(err) => { + println!("Error loading app preferences: {}", err); + Self::default() + } + } + } + + pub fn to_serializable(&self) -> AppPreferencesSerializable { + AppPreferencesSerializable { + preferred_download_directory: Some(self.preferred_download_directory.clone()), + play_sounds: Some(self.play_sounds), + } + } +} diff --git a/crates/desktop/artcraft/src/state/app_preferences/app_preferences_manager.rs b/crates/desktop/artcraft/src/state/app_preferences/app_preferences_manager.rs new file mode 100644 index 0000000000..1e6a626fe0 --- /dev/null +++ b/crates/desktop/artcraft/src/state/app_preferences/app_preferences_manager.rs @@ -0,0 +1,10 @@ +use crate::state::app_preferences::app_preferences::AppPreferences; +use crate::state::data_dir::app_data_root::AppDataRoot; +use memory_store::clone_cell::CloneCell; + +pub type AppPreferencesManager = CloneCell; + +pub fn load_app_preferences_or_default(data_root: &AppDataRoot) -> AppPreferencesManager { + let prefs = AppPreferences::load_from_file_or_default(data_root); + AppPreferencesManager::with_owned(prefs) +} diff --git a/crates/desktop/artcraft/src/state/app_preferences/app_preferences_serializable.rs b/crates/desktop/artcraft/src/state/app_preferences/app_preferences_serializable.rs new file mode 100644 index 0000000000..58ebc852e7 --- /dev/null +++ b/crates/desktop/artcraft/src/state/app_preferences/app_preferences_serializable.rs @@ -0,0 +1,48 @@ +use crate::state::app_preferences::app_preferences::AppPreferences; +use crate::state::app_preferences::preferred_download_directory::PreferredDownloadDirectory; +use crate::state::data_dir::app_data_root::AppDataRoot; +use errors::AnyhowResult; +use serde_derive::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct AppPreferencesSerializable { + /// The downloads directory to use when a user downloads a file. + pub preferred_download_directory: Option, + + /// Play sounds on events. + pub play_sounds: Option, +} + +impl AppPreferencesSerializable { + pub fn load_from_file(app_data_root: &AppDataRoot) -> AnyhowResult> { + let filename = app_data_root.settings_dir().get_app_preferences_path(); + if !filename.exists() { + return Ok(None); + } + + let contents = std::fs::read_to_string(filename)?; + let data: Self = serde_json::from_str(&contents)?; + Ok(Some(data)) + } + + pub fn save_to_file(&self, app_data_root: &AppDataRoot) -> AnyhowResult<()> { + let filename = app_data_root.settings_dir().get_app_preferences_path(); + let json = serde_json::to_string(self)?; + std::fs::write(filename, json)?; + Ok(()) + } + + pub fn to_preferences(&self) -> AppPreferences { + let mut preferences = AppPreferences::default(); + + if let Some(preferred_download_directory) = &self.preferred_download_directory { + preferences.preferred_download_directory = preferred_download_directory.clone(); + } + + if let Some(play_sounds) = self.play_sounds { + preferences.play_sounds = play_sounds; + } + + preferences + } +} diff --git a/crates/desktop/artcraft/src/state/app_preferences/mod.rs b/crates/desktop/artcraft/src/state/app_preferences/mod.rs new file mode 100644 index 0000000000..d2385da909 --- /dev/null +++ b/crates/desktop/artcraft/src/state/app_preferences/mod.rs @@ -0,0 +1,4 @@ +pub mod app_preferences; +pub mod app_preferences_manager; +pub mod app_preferences_serializable; +pub mod preferred_download_directory; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/state/app_preferences/preferred_download_directory.rs b/crates/desktop/artcraft/src/state/app_preferences/preferred_download_directory.rs new file mode 100644 index 0000000000..1b7f528ead --- /dev/null +++ b/crates/desktop/artcraft/src/state/app_preferences/preferred_download_directory.rs @@ -0,0 +1,100 @@ +use errors::AnyhowError; +use serde_derive::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use std::path::PathBuf; +use std::str::FromStr; + +const SYSTEM_DEFAULT_SENTINEL_VALUE: &str = "system_default"; + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum PreferredDownloadDirectory { + /// The system-default downloads directory, which varies by OS. + /// NB: Serializes as `"system_default"` in JSON. + SystemDefault, + + /// A user-defined path + /// NB: Serializes as `{"custom": "/path/to/dir"}` in JSON. + Custom(PathBuf), +} + +impl FromStr for PreferredDownloadDirectory { + type Err = AnyhowError; + + fn from_str(s: &str) -> Result { + if s == SYSTEM_DEFAULT_SENTINEL_VALUE || s == "\"system_default\"" { + Ok(PreferredDownloadDirectory::SystemDefault) + } else { + // TODO: If it gets serialized as JSON, we may need to remove the `{"custom": ...` wrapping layer. + let path = PathBuf::from(s); + Ok(PreferredDownloadDirectory::Custom(path)) + } + } +} + +impl Display for PreferredDownloadDirectory { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + PreferredDownloadDirectory::SystemDefault => write!(f, "{}", SYSTEM_DEFAULT_SENTINEL_VALUE), + PreferredDownloadDirectory::Custom(path) => write!(f, "{}", path.to_string_lossy()), + } + } +} + + +#[cfg(test)] +mod tests { + use crate::state::app_preferences::preferred_download_directory::PreferredDownloadDirectory; + use std::path::PathBuf; + use std::str::FromStr; + + mod json { + use super::*; + + #[test] + fn to_json_system_default() { + let val = PreferredDownloadDirectory::SystemDefault; + let val = serde_json::to_string(&val).unwrap(); + assert_eq!(&val, "\"system_default\""); + } + + #[test] + fn to_json_custom() { + let val = PreferredDownloadDirectory::Custom("/tmp".into()); + let val = serde_json::to_string(&val).unwrap(); + assert_eq!(&val, "{\"custom\":\"/tmp\"}"); + } + } + + mod string { + use super::*; + + #[test] + fn to_string_system_default() { + let val = PreferredDownloadDirectory::SystemDefault; + let val = val.to_string(); + assert_eq!(&val, "system_default"); + } + + #[test] + fn to_string_custom() { + let val = PreferredDownloadDirectory::Custom("/tmp/foo".into()); + let val = val.to_string(); + assert_eq!(&val, "/tmp/foo"); + } + } + + #[test] + fn from_string_system_default() { + let val = "system_default"; + let val = PreferredDownloadDirectory::from_str(val).unwrap(); + assert_eq!(val, PreferredDownloadDirectory::SystemDefault); + } + + #[test] + fn from_string_custom() { + let val = "/tmp/foo"; + let val = PreferredDownloadDirectory::from_str(val).unwrap(); + assert_eq!(val, PreferredDownloadDirectory::Custom(PathBuf::from("/tmp/foo"))); + } +} diff --git a/crates/desktop/artcraft/src/state/data_dir/app_data_root.rs b/crates/desktop/artcraft/src/state/data_dir/app_data_root.rs index b3c29855a6..c9e675681c 100644 --- a/crates/desktop/artcraft/src/state/data_dir/app_data_root.rs +++ b/crates/desktop/artcraft/src/state/data_dir/app_data_root.rs @@ -1,6 +1,7 @@ use crate::state::data_dir::app_assets_dir::AppAssetsDir; use crate::state::data_dir::app_credentials_dir::AppCredentialsDir; use crate::state::data_dir::app_downloads_dir::AppDownloadsDir; +use crate::state::data_dir::app_settings_dir::AppSettingsDir; use crate::state::data_dir::app_weights_dir::AppWeightsDir; use crate::state::data_dir::temporary_dir::TemporaryDir; use crate::state::data_dir::trait_data_subdir::DataSubdir; @@ -12,7 +13,6 @@ use std::path::{Path, PathBuf}; const DEFAULT_DATA_DIR : &str = "Artcraft"; - /// Note: Tauri appends ".log" to the end of the filename. const LOG_FILE_NAME : &str = "application_debug"; @@ -23,6 +23,7 @@ pub struct AppDataRoot { assets_dir: AppAssetsDir, credentials_dir: AppCredentialsDir, downloads_dir: AppDownloadsDir, + settings_dir: AppSettingsDir, weights_dir: AppWeightsDir, temp_dir: TemporaryDir, log_file_name: PathBuf, @@ -60,10 +61,11 @@ impl AppDataRoot { } let assets_dir = AppAssetsDir::get_or_create_in_root_dir(&dir)?; - let downloads_dir = AppDownloadsDir::get_or_create_in_root_dir(&dir)?; let credentials_dir = AppCredentialsDir::get_or_create_in_root_dir(&dir)?; - let weights_dir = AppWeightsDir::get_or_create_in_root_dir(&dir)?; + let downloads_dir = AppDownloadsDir::get_or_create_in_root_dir(&dir)?; + let settings_dir = AppSettingsDir::get_or_create_in_root_dir(&dir)?; let temp_dir = TemporaryDir::get_or_create_in_root_dir(&dir)?; + let weights_dir = AppWeightsDir::get_or_create_in_root_dir(&dir)?; let log_file_name = dir.join(LOG_FILE_NAME); let log_file_name_string = log_file_name .to_str() @@ -75,6 +77,7 @@ impl AppDataRoot { assets_dir, credentials_dir, downloads_dir, + settings_dir, weights_dir, temp_dir, log_file_name, @@ -94,6 +97,10 @@ impl AppDataRoot { &self.downloads_dir } + pub fn settings_dir(&self) -> &AppSettingsDir { + &self.settings_dir + } + pub fn weights_dir(&self) -> &AppWeightsDir { &self.weights_dir } diff --git a/crates/desktop/artcraft/src/state/data_dir/app_settings_dir.rs b/crates/desktop/artcraft/src/state/data_dir/app_settings_dir.rs new file mode 100644 index 0000000000..5c304c5815 --- /dev/null +++ b/crates/desktop/artcraft/src/state/data_dir/app_settings_dir.rs @@ -0,0 +1,51 @@ +use crate::state::app_preferences::app_preferences::AppPreferences; +use crate::state::data_dir::trait_data_subdir::DataSubdir; +use errors::AnyhowResult; +use std::fs::OpenOptions; +use std::io::Write; +use std::path::{Path, PathBuf}; + +/// For now, let's just keep a collection of loose json files in a settings dir. +/// Once we know the structure and schema, we can standardize on a single json file. +#[derive(Clone)] +pub struct AppSettingsDir { + path: PathBuf, +} + +impl DataSubdir for AppSettingsDir { + const DIRECTORY_NAME: &'static str = "settings"; + + fn new_from> (dir: P) -> Self { + Self { + path: dir.as_ref().to_path_buf(), + } + } + + fn path(&self) -> &Path { + &self.path + } +} + +impl AppSettingsDir { + pub fn get_app_preferences_path(&self) -> PathBuf { + self.path.join("app_preferences.json") + } + + pub fn write_app_preferences(&self, prefs: &AppPreferences) -> AnyhowResult<()> { + let serializable = prefs.to_serializable(); + let serializable = serde_json::to_string(&serializable)?; + + let filename = self.get_app_preferences_path(); + + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .open(filename)?; + + file.write_all(serializable.as_bytes())?; + file.flush()?; + + Ok(()) + } +} diff --git a/crates/desktop/artcraft/src/state/data_dir/mod.rs b/crates/desktop/artcraft/src/state/data_dir/mod.rs index 0ee920f63d..5f17f678a9 100644 --- a/crates/desktop/artcraft/src/state/data_dir/mod.rs +++ b/crates/desktop/artcraft/src/state/data_dir/mod.rs @@ -2,6 +2,7 @@ pub mod app_assets_dir; pub mod app_credentials_dir; pub mod app_data_root; pub mod app_downloads_dir; +pub mod app_settings_dir; pub mod app_weights_dir; pub mod temporary_dir; pub mod trait_data_subdir; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/state/mod.rs b/crates/desktop/artcraft/src/state/mod.rs index 4f79cf311e..6a4f3397a5 100644 --- a/crates/desktop/artcraft/src/state/mod.rs +++ b/crates/desktop/artcraft/src/state/mod.rs @@ -1,9 +1,8 @@ -pub mod app_config; +pub mod app_preferences; pub mod app_startup_time; pub mod data_dir; pub mod expanduser; pub mod main_window_size; pub mod os_platform; pub mod sora; -pub mod storyteller; -pub mod yaml_config; \ No newline at end of file +pub mod storyteller; \ No newline at end of file diff --git a/crates/desktop/artcraft/src/state/yaml_config.rs b/crates/desktop/artcraft/src/state/yaml_config.rs deleted file mode 100644 index 6a63db99ea..0000000000 --- a/crates/desktop/artcraft/src/state/yaml_config.rs +++ /dev/null @@ -1,52 +0,0 @@ -use filesys::recursively_find_file_by_name::recursively_find_file_by_name; -use std::fs::File; -use std::io::BufReader; -use std::path::Path; - -const CONFIG_FILENAME : &str = "app_config.yaml"; - -/// Configuration read or derived from a YAML config file at startup -/// This is not meant to contain any runtime config values. -/// This may not be exhaustive. -#[derive(serde::Deserialize, serde::Serialize, Clone, Debug, Default)] -pub struct YamlConfig { - pub image_height: Option, - pub image_width: Option, - pub scheduler_steps: Option, - pub scheduler_samples: Option, - pub seed: Option, - pub cfg_scale: Option, - - /// Override the default location to store data on Linux - pub linux_default_data_path: Option, - - /// Override the default location to store data on Windows - pub windows_default_data_path: Option, - - /// Override the default location to store data on Mac - pub mac_default_data_path: Option, -} - -impl YamlConfig { - pub fn read_from_file>(path: P) -> anyhow::Result { - let file = File::open(path)?; - let reader = BufReader::new(file); - let config = serde_yaml::from_reader(reader)?; - Ok(config) - } - - pub fn load_from_config_file_recursive() -> YamlConfig { - let maybe_path = recursively_find_file_by_name(CONFIG_FILENAME, "../../../", 5) - .ok() - .flatten(); - if let Some(path) = maybe_path { - let path = path.canonicalize().unwrap_or(path); - println!("Attempting to load configs from: {:?}", &path); - if let Ok(config) = YamlConfig::read_from_file(path) { - return config; - } - } - println!("Couldn't load config file. Using default configs."); - YamlConfig::default() - } -}