dioxus_sdk_storage/client_storage/
fs.rs1use crate::{StorageChannelPayload, StorageSubscription};
2use dioxus::logger::tracing::trace;
3use serde::Serialize;
4use serde::de::DeserializeOwned;
5use std::collections::HashMap;
6use std::io::Write;
7use std::sync::{OnceLock, RwLock};
8use tokio::sync::watch::{Receiver, channel};
9
10use crate::{StorageBacking, StorageSubscriber, serde_to_string, try_serde_from_string};
11
12#[doc(hidden)]
13pub fn set_directory(path: std::path::PathBuf) {
15 LOCATION.set(path).unwrap();
16}
17
18#[doc(hidden)]
19pub fn set_dir_name(name: &str) {
20 set_directory(
21 directories::BaseDirs::new()
22 .unwrap()
23 .data_local_dir()
24 .join(name),
25 )
26}
27
28static LOCATION: OnceLock<std::path::PathBuf> = OnceLock::new();
30
31fn set<T: Serialize>(key: String, value: &T) {
33 let as_str = serde_to_string(value);
34 let path = LOCATION
35 .get()
36 .expect("Call the set_dir macro before accessing persistant data");
37 std::fs::create_dir_all(path).unwrap();
38 let file_path = path.join(key);
39 let mut file = std::fs::File::create(file_path).unwrap();
40 file.write_all(as_str.as_bytes()).unwrap();
41}
42
43fn get<T: DeserializeOwned>(key: &str) -> Option<T> {
45 let path = LOCATION
46 .get()
47 .expect("Call the set_dir macro before accessing persistant data")
48 .join(key);
49 let s = std::fs::read_to_string(path).ok()?;
50 try_serde_from_string(&s)
51}
52
53#[derive(Clone)]
54pub struct LocalStorage;
55
56impl StorageBacking for LocalStorage {
57 type Key = String;
58
59 fn set<T: Serialize + Send + Sync + Clone + 'static>(key: String, value: &T) {
60 let key_clone = key.clone();
61 let value_clone = (*value).clone();
62 set(key, value);
63
64 if let Some(subscriptions) = SUBSCRIPTIONS.get() {
66 let read_binding = subscriptions.read().unwrap();
67 if let Some(subscription) = read_binding.get(&key_clone) {
68 subscription
69 .tx
70 .send(StorageChannelPayload::new(value_clone))
71 .unwrap();
72 }
73 }
74 }
75
76 fn get<T: DeserializeOwned>(key: &String) -> Option<T> {
77 get(key)
78 }
79}
80
81impl StorageSubscriber<LocalStorage> for LocalStorage {
85 fn subscribe<T: DeserializeOwned + Send + Sync + Clone + 'static>(
86 key: &<LocalStorage as StorageBacking>::Key,
87 ) -> Receiver<StorageChannelPayload> {
88 let subscriptions = SUBSCRIPTIONS.get_or_init(|| RwLock::new(HashMap::new()));
90
91 let read_binding = subscriptions.read().unwrap();
94 match read_binding.get(key) {
95 Some(subscription) => subscription.tx.subscribe(),
96 None => {
97 drop(read_binding);
98 let (tx, rx) = channel::<StorageChannelPayload>(StorageChannelPayload::default());
99 let subscription = StorageSubscription::new::<LocalStorage, T>(tx, key.clone());
100
101 subscriptions
102 .write()
103 .unwrap()
104 .insert(key.clone(), subscription);
105 rx
106 }
107 }
108 }
109
110 fn unsubscribe(key: &<LocalStorage as StorageBacking>::Key) {
111 trace!("Unsubscribing from \"{}\"", key);
112
113 if let Some(subscriptions) = SUBSCRIPTIONS.get() {
115 let read_binding = subscriptions.read().unwrap();
116
117 if read_binding.contains_key(key) {
119 trace!("Found entry for \"{}\"", key);
120 drop(read_binding);
121 subscriptions.write().unwrap().remove(key);
122 }
123 }
124 }
125}
126
127static SUBSCRIPTIONS: OnceLock<RwLock<HashMap<String, StorageSubscription>>> = OnceLock::new();