use crate::{StorageChannelPayload, StorageSubscription};
use dioxus::logger::tracing::trace;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::io::Write;
use std::sync::{OnceLock, RwLock};
use tokio::sync::watch::{Receiver, channel};
use crate::{StorageBacking, StorageSubscriber, serde_to_string, try_serde_from_string};
#[doc(hidden)]
pub fn set_directory(path: std::path::PathBuf) {
LOCATION.set(path).unwrap();
}
#[doc(hidden)]
pub fn set_dir_name(name: &str) {
set_directory(
directories::BaseDirs::new()
.unwrap()
.data_local_dir()
.join(name),
)
}
static LOCATION: OnceLock<std::path::PathBuf> = OnceLock::new();
fn set<T: Serialize>(key: String, value: &T) {
let as_str = serde_to_string(value);
let path = LOCATION
.get()
.expect("Call the set_dir macro before accessing persistant data");
std::fs::create_dir_all(path).unwrap();
let file_path = path.join(key);
let mut file = std::fs::File::create(file_path).unwrap();
file.write_all(as_str.as_bytes()).unwrap();
}
fn get<T: DeserializeOwned>(key: &str) -> Option<T> {
let path = LOCATION
.get()
.expect("Call the set_dir macro before accessing persistant data")
.join(key);
let s = std::fs::read_to_string(path).ok()?;
try_serde_from_string(&s)
}
#[derive(Clone)]
pub struct LocalStorage;
impl StorageBacking for LocalStorage {
type Key = String;
fn set<T: Serialize + Send + Sync + Clone + 'static>(key: String, value: &T) {
let key_clone = key.clone();
let value_clone = (*value).clone();
set(key, value);
if let Some(subscriptions) = SUBSCRIPTIONS.get() {
let read_binding = subscriptions.read().unwrap();
if let Some(subscription) = read_binding.get(&key_clone) {
subscription
.tx
.send(StorageChannelPayload::new(value_clone))
.unwrap();
}
}
}
fn get<T: DeserializeOwned>(key: &String) -> Option<T> {
get(key)
}
}
impl StorageSubscriber<LocalStorage> for LocalStorage {
fn subscribe<T: DeserializeOwned + Send + Sync + Clone + 'static>(
key: &<LocalStorage as StorageBacking>::Key,
) -> Receiver<StorageChannelPayload> {
let subscriptions = SUBSCRIPTIONS.get_or_init(|| RwLock::new(HashMap::new()));
let read_binding = subscriptions.read().unwrap();
match read_binding.get(key) {
Some(subscription) => subscription.tx.subscribe(),
None => {
drop(read_binding);
let (tx, rx) = channel::<StorageChannelPayload>(StorageChannelPayload::default());
let subscription = StorageSubscription::new::<LocalStorage, T>(tx, key.clone());
subscriptions
.write()
.unwrap()
.insert(key.clone(), subscription);
rx
}
}
}
fn unsubscribe(key: &<LocalStorage as StorageBacking>::Key) {
trace!("Unsubscribing from \"{}\"", key);
if let Some(subscriptions) = SUBSCRIPTIONS.get() {
let read_binding = subscriptions.read().unwrap();
if read_binding.contains_key(key) {
trace!("Found entry for \"{}\"", key);
drop(read_binding);
subscriptions.write().unwrap().remove(key);
}
}
}
}
static SUBSCRIPTIONS: OnceLock<RwLock<HashMap<String, StorageSubscription>>> = OnceLock::new();