diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 8b811dc..fa48eb6 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -584,6 +584,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "test_dir", "time", "tokio", "tracing", @@ -3165,6 +3166,15 @@ dependencies = [ "utf-8", ] +[[package]] +name = "test_dir" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc19daf9fc57fadcf740c4abaaa0cd08d9ce22a2a0629aaf6cbd9ae4b80683a" +dependencies = [ + "rand 0.8.5", +] + [[package]] name = "thin-slice" version = "0.1.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 69306a8..25ea562 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -41,3 +41,6 @@ default = [ "custom-protocol" ] # this feature is used used for production builds where `devPath` points to the filesystem # DO NOT remove this custom-protocol = [ "tauri/custom-protocol" ] + +[dev-dependencies] +test_dir = "0.2.0" diff --git a/src-tauri/src/picker/config.rs b/src-tauri/src/picker/config/display_config.rs similarity index 83% rename from src-tauri/src/picker/config.rs rename to src-tauri/src/picker/config/display_config.rs index 2b1dea5..09586e8 100644 --- a/src-tauri/src/picker/config.rs +++ b/src-tauri/src/picker/config/display_config.rs @@ -1,11 +1,13 @@ -#[derive(Clone, Copy)] +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct LedStripConfig { pub index: usize, pub global_start_position: usize, pub global_end_position: usize, } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Serialize, Deserialize)] pub struct DisplayConfig { pub index_of_display: usize, pub display_width: usize, @@ -16,17 +18,6 @@ pub struct DisplayConfig { pub right_led_strip: LedStripConfig, } -#[derive(Clone, Copy)] -pub enum LedFlowX { - LR, // from left to right - RL, // from right to left -} -#[derive(Clone, Copy)] -pub enum LedFlowY { - TB, // from top to bottom - BT, // from bottom to top -} - impl DisplayConfig { pub fn default(index_of_display: usize, display_width: usize, display_height: usize) -> Self { Self { diff --git a/src-tauri/src/picker/config/manger.rs b/src-tauri/src/picker/config/manger.rs new file mode 100644 index 0000000..25c090d --- /dev/null +++ b/src-tauri/src/picker/config/manger.rs @@ -0,0 +1,122 @@ +use std::{ + env::current_dir, + fs::{self, File}, + io::Read, + path::PathBuf, +}; + +use once_cell::sync::OnceCell; +use serde::{Deserialize, Serialize}; +use tauri::api::path::config_dir; + +use super::DisplayConfig; + +#[derive(Serialize, Deserialize, Clone)] +pub struct Configuration { + config_version: u8, + display_configs: Vec, +} + +impl Configuration { + pub fn default() -> Self { + Self { + config_version: 1, + display_configs: vec![], + } + } +} + +#[derive(Serialize, Deserialize)] +pub struct Manager { + config: Configuration, +} + +impl Manager { + pub fn global() -> &'static Manager { + static DISPLAY_CONFIG_MANAGE: OnceCell = OnceCell::new(); + + DISPLAY_CONFIG_MANAGE.get_or_init(|| Self::init_from_disk()) + } + + pub fn default() -> Self { + Self::new(Configuration::default()) + } + + pub fn new(config: Configuration) -> Self { + Self { config } + } + + pub fn get_config_file_path() -> PathBuf { + config_dir() + .unwrap_or(current_dir().unwrap()) + .join("display_config.json") + } + + pub fn init_from_disk() -> Self { + let config_file_path = Self::get_config_file_path(); + match Self::read_config_from_disk(config_file_path) { + Ok(config) => Self::new(config), + Err(_) => Self::default(), + } + } + + pub fn read_config_from_disk(config_file_path: PathBuf) -> anyhow::Result { + let mut file = File::open(config_file_path) + .map_err(|error| anyhow::anyhow!("config file is not existed. {}", error))?; + let mut contents = String::new(); + file.read_to_string(&mut contents) + .map_err(|error| anyhow::anyhow!("can not read config file. {}", error))?; + serde_json::from_str(&contents) + .map_err(|error| anyhow::anyhow!("can not parse config file contents. {}", error)) + } + + pub fn write_config_to_disk(&self, config_file_path: PathBuf) -> anyhow::Result<()> { + let contents = serde_json::to_string(&self.config) + .map_err(|error| anyhow::anyhow!("can not serialize config. {}", error))?; + fs::write(config_file_path, contents.as_bytes()) + .map_err(|error| anyhow::anyhow!("can not write config file. {}", error))?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + + use std::fs; + + use serde_json::json; + use test_dir::{DirBuilder, TestDir}; + + use crate::picker::config::Configuration; + + #[test] + fn write_config_to_disk_should_be_successful() { + let temp = TestDir::temp().create("config_dir", test_dir::FileType::Dir); + let config_file_path = temp.path("config_dir").join("picker.config.json"); + let manager = crate::picker::config::manger::Manager::default(); + manager + .write_config_to_disk(config_file_path.clone()) + .unwrap(); + + let contents = fs::read_to_string(config_file_path.clone()).unwrap(); + let _config: Configuration = serde_json::from_str(contents.as_str()).unwrap(); + } + + #[test] + fn read_config_to_disk_should_be_successful() { + let temp = TestDir::temp().create("config_dir", test_dir::FileType::Dir); + let config_file_path = temp.path("config_dir").join("picker.config.json"); + fs::write( + config_file_path.clone(), + json!({ + "config_version": 1, + "display_configs": [] + }) + .to_string() + .as_bytes(), + ).unwrap(); + let _manager = + crate::picker::config::manger::Manager::read_config_from_disk(config_file_path.clone()) + .unwrap(); + } +} diff --git a/src-tauri/src/picker/config/mod.rs b/src-tauri/src/picker/config/mod.rs new file mode 100644 index 0000000..f1c53a0 --- /dev/null +++ b/src-tauri/src/picker/config/mod.rs @@ -0,0 +1,5 @@ +mod display_config; +mod manger; + +pub use display_config::*; +pub use manger::*; \ No newline at end of file diff --git a/src-tauri/src/picker/manager.rs b/src-tauri/src/picker/manager.rs index 328d461..9157103 100644 --- a/src-tauri/src/picker/manager.rs +++ b/src-tauri/src/picker/manager.rs @@ -5,10 +5,7 @@ use scrap::Display; use std::sync::Arc; use tokio::{sync::Mutex, task}; -use crate::picker::{ - config::{LedFlowX, LedFlowY, LedStripConfig}, - screen::Screen, -}; +use crate::picker::{config::LedStripConfig, screen::Screen}; use super::{ config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor,