use std::{collections::VecDeque, fs::DirBuilder, path::PathBuf, sync::Arc}; use tauri::{path::PathResolver, AppHandle, Manager, Runtime}; use tokio::sync::Mutex; use uuid::Uuid; use crate::pattern::Pattern; static PATTERN_KEY_PREFIX: &[u8] = b"PATTERN:"; fn ensure_dir(resolver: &PathResolver) -> anyhow::Result { let config_dir = resolver .local_data_dir() .map_err(|e| anyhow::anyhow!("Unable to get local data directory: {}", e))?; if !config_dir.exists() { let mut dir_creator = DirBuilder::new(); dir_creator .recursive(true) .create(&config_dir) .map_err(|e| anyhow::anyhow!("Unable to create config directory: {}", e))?; } Ok(config_dir) } pub struct ConfigDb(Arc>); impl ConfigDb { pub fn open(app_handle: &AppHandle) -> anyhow::Result { let config_dir = ensure_dir(app_handle.path())?; let db_path = config_dir.join("conf.db"); let db = sled::open(&db_path) .map_err(|e| anyhow::anyhow!("Unable to open config database: {}", e))?; Ok(Self(Arc::new(Mutex::new(db)))) } pub async fn store_pattern(&self, pattern: &Pattern) -> anyhow::Result<()> { let db = self.0.lock().await; let key = PATTERN_KEY_PREFIX .iter() .chain(pattern.id.as_bytes()) .cloned() .collect::>(); db.insert(key, pattern) .map_err(|e| anyhow::anyhow!("Unable to store pattern: {}", e))?; db.flush_async() .await .map_err(|e| anyhow::anyhow!("Unable to save db: {}", e))?; Ok(()) } pub async fn get_patterns(&self, keyword: Option) -> anyhow::Result> { let db = self.0.lock().await; let patterns = db.scan_prefix(PATTERN_KEY_PREFIX); let mut result: VecDeque = VecDeque::new(); for entry in patterns { if let Ok((_, pattern)) = entry { let pattern = Into::::into(pattern); if let Some(keyword) = &keyword { if !pattern.name.contains(keyword) { continue; } } let index = result.binary_search_by(|item| item.created_at.cmp(&pattern.created_at)); match index { Ok(index) => result.insert(index + 1, pattern), Err(index) => result.insert(index, pattern), }; } } Ok(result) } pub async fn get_pattern(&self, pattern_id: &Uuid) -> anyhow::Result> { let db = self.0.lock().await; let key = PATTERN_KEY_PREFIX .iter() .chain(pattern_id.as_bytes()) .cloned() .collect::>(); let pattern = db .get(key) .map_err(|e| anyhow::anyhow!("Unable to get pattern: {}", e))? .map(|p| Into::::into(p)); Ok(pattern) } pub async fn remove_pattern(&self, pattern_id: &Uuid) -> anyhow::Result<()> { let db = self.0.lock().await; let key = PATTERN_KEY_PREFIX .iter() .chain(pattern_id.as_bytes()) .cloned() .collect::>(); db.remove(key) .map_err(|e| anyhow::anyhow!("Unable to remove pattern: {}", e))?; // todo: need to remove requested pattern in all playlists. db.flush_async() .await .map_err(|e| anyhow::anyhow!("Unable to save db: {}", e))?; Ok(()) } }