tauri_store/store/
save.rs1use super::StoreId;
2use crate::manager::ManagerExt;
3use futures::future::BoxFuture;
4use serde::ser::SerializeTuple;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use serde_json::Value as Json;
7use std::fmt;
8use std::result::Result as StdResult;
9use std::time::Duration;
10use tauri::async_runtime::spawn_blocking;
11use tauri::{AppHandle, Runtime};
12use tauri_store_utils::{Debounce, RemoteCallable, Throttle};
13
14type RemoteSaveHandle<R> = Box<dyn RemoteCallable<AppHandle<R>> + Send + Sync>;
15type SaveHandleFn<R> = Box<dyn Fn(AppHandle<R>) -> BoxFuture<'static, ()> + Send + Sync + 'static>;
16
17pub(super) struct SaveHandle<R: Runtime>(RemoteSaveHandle<R>);
18
19impl<R: Runtime> SaveHandle<R> {
20 pub fn call(&self, app: &AppHandle<R>) {
21 self.0.call(app);
22 }
23
24 pub fn abort(&self) {
25 self.0.abort();
26 }
27}
28
29pub(super) fn debounce<R: Runtime>(duration: Duration, id: StoreId) -> SaveHandle<R> {
30 SaveHandle(Box::new(Debounce::new(duration, save_handle(id))))
31}
32
33pub(super) fn throttle<R: Runtime>(duration: Duration, id: StoreId) -> SaveHandle<R> {
34 SaveHandle(Box::new(Throttle::new(duration, save_handle(id))))
35}
36
37fn save_handle<R: Runtime>(id: StoreId) -> SaveHandleFn<R> {
38 Box::new(move |app| {
39 let id = id.clone();
40 Box::pin(async move {
41 let task = spawn_blocking(move || {
42 app
43 .store_collection()
44 .get_resource(&id)?
45 .locked(|store| store.save_now())
46 });
47
48 let _ = task.await;
49 })
50 })
51}
52
53#[non_exhaustive]
58#[derive(Clone, Copy, Debug, Default)]
59pub enum SaveStrategy {
60 #[default]
61 Immediate,
62 Debounce(Duration),
63 Throttle(Duration),
64}
65
66impl SaveStrategy {
67 const IMMEDIATE: &'static str = "immediate";
68 const DEBOUNCE: &'static str = "debounce";
69 const THROTTLE: &'static str = "throttle";
70
71 pub const fn debounce_millis(millis: u64) -> Self {
73 Self::Debounce(Duration::from_millis(millis))
74 }
75
76 pub const fn debounce_secs(secs: u64) -> Self {
78 Self::Debounce(Duration::from_secs(secs))
79 }
80
81 pub const fn throttle_millis(millis: u64) -> Self {
83 Self::Throttle(Duration::from_millis(millis))
84 }
85
86 pub const fn throttle_secs(secs: u64) -> Self {
88 Self::Throttle(Duration::from_secs(secs))
89 }
90
91 pub const fn is_debounce(&self) -> bool {
93 matches!(self, Self::Debounce(_))
94 }
95
96 pub const fn is_throttle(&self) -> bool {
98 matches!(self, Self::Throttle(_))
99 }
100}
101
102impl fmt::Display for SaveStrategy {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Self::Immediate => write!(f, "{}", Self::IMMEDIATE),
106 Self::Debounce(_) => write!(f, "{}", Self::DEBOUNCE),
107 Self::Throttle(_) => write!(f, "{}", Self::THROTTLE),
108 }
109 }
110}
111
112impl Serialize for SaveStrategy {
113 fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
114 where
115 S: Serializer,
116 {
117 let interval = match self {
118 Self::Immediate => 0,
119 Self::Debounce(duration) | Self::Throttle(duration) => duration.as_millis(),
120 };
121
122 let mut tuple = serializer.serialize_tuple(2)?;
123 tuple.serialize_element(&self.to_string())?;
124 tuple.serialize_element(&interval.to_string())?;
125 tuple.end()
126 }
127}
128
129impl<'de> Deserialize<'de> for SaveStrategy {
130 fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
131 where
132 D: Deserializer<'de>,
133 {
134 let err = || {
135 use serde::de::Error;
136 D::Error::custom("invalid save strategy")
137 };
138
139 let value = Json::deserialize(deserializer)?;
140 if let Json::Array(mut array) = value {
141 if array.len() != 2 {
142 return Err(err());
143 }
144
145 let strategy = array
146 .remove(0)
147 .as_str()
148 .map(ToOwned::to_owned)
149 .ok_or_else(err)?;
150
151 let duration = array
152 .remove(0)
153 .as_str()
154 .map(str::parse)
155 .ok_or_else(err)?
156 .map(Duration::from_millis)
157 .map_err(|_| err())?;
158
159 if duration.is_zero() {
160 return Ok(Self::Immediate);
161 }
162
163 match strategy.as_str() {
164 Self::DEBOUNCE => Ok(Self::Debounce(duration)),
165 Self::THROTTLE => Ok(Self::Throttle(duration)),
166 Self::IMMEDIATE => Ok(Self::Immediate),
167 _ => Err(err()),
168 }
169 } else {
170 Err(err())
171 }
172 }
173}