From 51f4e74954489ff7d6326f5dda09d7adda675137 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Sun, 4 May 2025 13:33:43 -0400 Subject: [PATCH] preferences serialization and saving --- .../update_app_preference_command.rs | 65 +++++++++++++ .../preferred_download_directory.rs | 96 ++++++++++++++++++- 2 files changed, 159 insertions(+), 2 deletions(-) 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 index e69de29bb2..83e00f514c 100644 --- 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 @@ -0,0 +1,65 @@ +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::{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>, +) -> Result { + info!("update_app_preferences_command called"); + + update_prefs(request, &app_prefs) + .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) -> 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)?; + + // TODO: Write to filesystem. + + Ok(()) +} 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 index a4b9cc392e..1b7f528ead 100644 --- a/crates/desktop/artcraft/src/state/app_preferences/preferred_download_directory.rs +++ b/crates/desktop/artcraft/src/state/app_preferences/preferred_download_directory.rs @@ -1,8 +1,100 @@ -use std::path::PathBuf; +use errors::AnyhowError; use serde_derive::{Deserialize, Serialize}; +use std::fmt::{Display, Formatter}; +use std::path::PathBuf; +use std::str::FromStr; -#[derive(Debug, Clone, Serialize, Deserialize)] +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"))); + } +}