Skip to main content

tauri_store_utils/time/
debounce.rs

1use crate::manager::ManagerExt;
2use crate::task::{OptionalAbortHandle, RemoteCallable};
3use parking_lot::Mutex;
4use std::future::Future;
5use std::sync::{Arc, Weak};
6use std::time::Duration;
7use tauri::{AppHandle, Runtime};
8use tokio::select;
9use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
10use tokio::task::AbortHandle;
11use tokio::time::sleep;
12
13type DebouncedFn<R, Fut> = dyn Fn(AppHandle<R>) -> Fut + Send + Sync + 'static;
14
15/// Debounces a function call.
16pub struct Debounce<R, T, Fut>
17where
18  R: Runtime,
19  T: Send + 'static,
20  Fut: Future<Output = T> + Send + 'static,
21{
22  inner: Arc<DebouncedFn<R, Fut>>,
23  sender: Arc<OptionalSender>,
24  abort_handle: Arc<OptionalAbortHandle>,
25  duration: Duration,
26}
27
28impl<R, T, Fut> Debounce<R, T, Fut>
29where
30  R: Runtime,
31  T: Send + 'static,
32  Fut: Future<Output = T> + Send + 'static,
33{
34  pub fn new<F>(duration: Duration, f: F) -> Self
35  where
36    F: Fn(AppHandle<R>) -> Fut + Send + Sync + 'static,
37  {
38    Self {
39      inner: Arc::new(f),
40      sender: Arc::new(OptionalSender::default()),
41      abort_handle: Arc::new(OptionalAbortHandle::default()),
42      duration,
43    }
44  }
45
46  pub fn call(&self, app: &AppHandle<R>) {
47    if self.sender.send() {
48      return;
49    }
50
51    let (tx, rx) = unbounded_channel();
52    let actor = Actor {
53      function: Arc::downgrade(&self.inner),
54      receiver: rx,
55      duration: self.duration,
56    };
57
58    self.sender.inner.lock().replace(tx);
59    self.abort_handle.replace(actor.run(app));
60  }
61
62  pub fn abort(&self) {
63    self.sender.inner.lock().take();
64    self.abort_handle.abort();
65  }
66}
67
68impl<R, T, Fut> RemoteCallable<AppHandle<R>> for Debounce<R, T, Fut>
69where
70  R: Runtime,
71  T: Send + 'static,
72  Fut: Future<Output = T> + Send + 'static,
73{
74  fn call(&self, app: &AppHandle<R>) {
75    self.call(app);
76  }
77
78  fn abort(&self) {
79    self.abort();
80  }
81}
82
83impl<R, T, Fut> Drop for Debounce<R, T, Fut>
84where
85  R: Runtime,
86  T: Send + 'static,
87  Fut: Future<Output = T> + Send + 'static,
88{
89  fn drop(&mut self) {
90    self.abort();
91  }
92}
93
94struct Actor<R, T, Fut>
95where
96  R: Runtime,
97  T: Send + 'static,
98  Fut: Future<Output = T> + Send + 'static,
99{
100  function: Weak<DebouncedFn<R, Fut>>,
101  receiver: UnboundedReceiver<Message>,
102  duration: Duration,
103}
104
105impl<R, T, Fut> Actor<R, T, Fut>
106where
107  R: Runtime,
108  T: Send + 'static,
109  Fut: Future<Output = T> + Send + 'static,
110{
111  fn run(mut self, app: &AppHandle<R>) -> AbortHandle {
112    app.spawn(move |app| async move {
113      loop {
114        select! {
115          message = self.receiver.recv() => {
116            if message.is_none() { break }
117          }
118          () = sleep(self.duration) => {
119            self.receiver.close();
120            if let Some(f) = self.function.upgrade() {
121              (f)(app).await;
122            }
123
124            break;
125          }
126        }
127      }
128    })
129  }
130}
131
132#[derive(Default)]
133pub(super) struct OptionalSender {
134  pub(super) inner: Mutex<Option<UnboundedSender<Message>>>,
135}
136
137impl OptionalSender {
138  pub(super) fn send(&self) -> bool {
139    self
140      .inner
141      .lock()
142      .as_ref()
143      .map(|it| it.send(Message::Call).is_ok())
144      .unwrap_or(false)
145  }
146}
147
148pub(super) enum Message {
149  Call,
150}