1use std::hash::Hash;
8use std::marker::PhantomData;
9use std::sync::Arc;
10use std::time::Duration;
11
12use crossbeam_channel::Receiver;
13use key_paths_core::{FieldDiff, hash_value};
14use parking_lot::RwLock;
15
16use crate::effect::EffectId;
17use crate::optics::Casepath;
18use crate::runtime::binding::{ReadStateBinding, RwStateBinding};
19use crate::runtime::state_access::StateRead;
20use crate::runtime::store::{ChangeSet, StoreHub, StoreTask, StoreWork};
21use key_paths_core::RefKpTrait;
22
23pub(crate) struct RwStoreBackend<S, M> {
24 pub state: Arc<RwLock<S>>,
25 pub hub: Arc<StoreHub<S, M>>,
26}
27
28impl<S, M> Clone for RwStoreBackend<S, M>
29where
30 S: Send + 'static,
31 M: Send + 'static,
32{
33 fn clone(&self) -> Self {
34 Self {
35 state: self.state.clone(),
36 hub: self.hub.clone(),
37 }
38 }
39}
40
41impl<S, M> StoreWork<S, M> for RwStoreBackend<S, M>
42where
43 S: Send + 'static,
44 M: Send + 'static,
45{
46 fn hub(&self) -> &Arc<StoreHub<S, M>> {
47 &self.hub
48 }
49}
50
51impl<S, M> RwStoreBackend<S, M>
52where
53 S: Send + 'static,
54 M: Send + 'static,
55{
56 pub fn new(state: Arc<RwLock<S>>, hub: Arc<StoreHub<S, M>>) -> Self {
57 Self { state, hub }
58 }
59
60 pub fn rw_store(&self) -> RwStore<S, M> {
61 RwStore {
62 backend: self.clone(),
63 }
64 }
65
66 pub fn read_store(&self) -> ReadStore<S, M> {
67 ReadStore {
68 backend: self.clone(),
69 }
70 }
71}
72
73pub struct RwStore<S, M> {
75 pub(crate) backend: RwStoreBackend<S, M>,
76}
77
78impl<S, M> Clone for RwStore<S, M>
79where
80 S: Send + 'static,
81 M: Send + 'static,
82{
83 fn clone(&self) -> Self {
84 Self {
85 backend: self.backend.clone(),
86 }
87 }
88}
89
90impl<S, M> RwStore<S, M>
91where
92 S: Send + Sync + Clone + 'static,
93 M: Send + 'static,
94{
95 pub fn send(&self, action: M) -> StoreTask {
96 let (tx, rx) = crossbeam_channel::bounded(1);
97 self.backend.hub.effect_done.lock().push_back(tx);
98 self.backend.begin_store_work();
99 let _ = self.backend.hub.sender.send_blocking(action);
100 StoreTask::from_rx(rx)
101 }
102
103 pub fn dispatch(&self, action: M) {
104 let _ = self.send(action);
105 }
106
107 pub fn state(&self) -> S {
108 self.backend.state.with_read(|s| s.clone())
109 }
110
111 pub fn binding(&self) -> RwStateBinding<S> {
112 RwStateBinding::new(Arc::clone(&self.backend.state))
113 }
114
115 pub fn read_store(&self) -> ReadStore<S, M> {
117 self.backend.read_store()
118 }
119
120 pub fn cancel(&self, id: EffectId) {
121 if let Some(handle) = self
122 .backend
123 .hub
124 .interpreter
125 .cancel_tokens
126 .lock()
127 .remove(&id)
128 {
129 handle.abort();
130 }
131 }
132
133 pub fn subscribe_state(&self) -> RwStateSubscriber<S>
134 where
135 S: Clone + PartialEq,
136 {
137 let (tx, rx) = crossbeam_channel::unbounded();
138 self.backend.hub.state_listeners.lock().push(tx);
139 RwStateSubscriber {
140 rx,
141 state: self.backend.state.clone(),
142 last: Some(Arc::new(self.backend.state.with_read(|s| s.clone()))),
143 }
144 }
145
146 pub fn subscribe_changes(&self) -> RwChangeSubscriber<S>
147 where
148 S: FieldDiff + Hash,
149 {
150 let (tx, rx) = crossbeam_channel::unbounded();
151 self.backend.hub.state_listeners.lock().push(tx);
152 let (last_hash, last_fields) = self.backend.state.with_read(|guard| {
153 let root = hash_value(guard);
154 let mut fields = Vec::new();
155 guard.field_hashes(&mut fields);
156 (Some(root), fields)
157 });
158 RwChangeSubscriber {
159 rx,
160 state: self.backend.state.clone(),
161 last_hash,
162 last_fields,
163 _marker: PhantomData,
164 }
165 }
166
167 pub fn scope<CS, CM, AK, SK>(
168 &self,
169 state_kp: SK,
170 action_kp: AK,
171 ) -> ScopedRwStore<S, M, CS, CM, AK, SK>
172 where
173 CS: Clone + PartialEq + Send + Sync + 'static,
174 CM: Clone + Send + 'static,
175 S: 'static,
176 M: 'static,
177 AK: Casepath<M, CM> + Clone + Send + Sync + 'static,
178 SK: RefKpTrait<S, CS> + Clone,
179 {
180 ScopedRwStore {
181 store: self.clone(),
182 state_kp,
183 action_kp,
184 _marker: PhantomData,
185 }
186 }
187}
188
189pub struct ReadStore<S, M> {
191 pub(crate) backend: RwStoreBackend<S, M>,
192}
193
194impl<S, M> Clone for ReadStore<S, M>
195where
196 S: Send + 'static,
197 M: Send + 'static,
198{
199 fn clone(&self) -> Self {
200 Self {
201 backend: self.backend.clone(),
202 }
203 }
204}
205
206impl<S, M> ReadStore<S, M>
207where
208 S: Send + Sync + Clone + 'static,
209 M: Send + 'static,
210{
211 pub fn with_read<R>(&self, f: impl FnOnce(&S) -> R) -> R {
212 self.backend.state.with_read(f)
213 }
214
215 pub fn state(&self) -> S {
216 self.with_read(|s| s.clone())
217 }
218
219 pub fn read_binding(&self) -> ReadStateBinding<S> {
220 ReadStateBinding::new(Arc::clone(&self.backend.state))
221 }
222
223 pub fn subscribe_state(&self) -> RwStateSubscriber<S>
224 where
225 S: Clone + PartialEq,
226 {
227 self.backend.rw_store().subscribe_state()
228 }
229
230 pub fn subscribe_changes(&self) -> RwChangeSubscriber<S>
231 where
232 S: FieldDiff + Hash,
233 {
234 self.backend.rw_store().subscribe_changes()
235 }
236}
237
238pub struct RwStateSubscriber<S> {
239 rx: Receiver<()>,
240 state: Arc<RwLock<S>>,
241 last: Option<Arc<S>>,
242}
243
244impl<S: PartialEq + Clone> RwStateSubscriber<S> {
245 #[allow(clippy::should_implement_trait)]
246 pub fn next(&mut self) -> Option<Arc<S>> {
247 loop {
248 match self.rx.try_recv() {
249 Ok(()) => {
250 while self.rx.try_recv().is_ok() {}
251 let snapshot = Arc::new(self.state.with_read(|s| s.clone()));
252 if self
253 .last
254 .as_ref()
255 .is_some_and(|prev| prev.as_ref() == snapshot.as_ref())
256 {
257 continue;
258 }
259 self.last = Some(snapshot.clone());
260 return Some(snapshot);
261 }
262 Err(crossbeam_channel::TryRecvError::Empty) => return None,
263 Err(crossbeam_channel::TryRecvError::Disconnected) => return None,
264 }
265 }
266 }
267
268 pub fn wait_next(&mut self, timeout: Duration) -> Option<Arc<S>> {
269 let deadline = std::time::Instant::now() + timeout;
270 while std::time::Instant::now() < deadline {
271 if let Some(s) = self.next() {
272 return Some(s);
273 }
274 std::thread::sleep(Duration::from_millis(5));
275 }
276 None
277 }
278
279 pub fn latest(&self) -> Option<Arc<S>> {
280 self.last.clone()
281 }
282
283 pub fn read_binding(&self) -> ReadStateBinding<S> {
284 ReadStateBinding::new(Arc::clone(&self.state))
285 }
286}
287
288pub struct RwChangeSubscriber<S>
289where
290 S: FieldDiff + Hash,
291{
292 rx: Receiver<()>,
293 state: Arc<RwLock<S>>,
294 last_hash: Option<u64>,
295 last_fields: Vec<(S::Path, u64)>,
296 _marker: PhantomData<S>,
297}
298
299impl<S> RwChangeSubscriber<S>
300where
301 S: FieldDiff + Hash,
302{
303 fn snapshot_fields(&self) -> (u64, Vec<(S::Path, u64)>) {
304 self.state.with_read(|guard| {
305 let root = hash_value(guard);
306 let mut fields = Vec::new();
307 guard.field_hashes(&mut fields);
308 (root, fields)
309 })
310 }
311
312 fn diff_fields(&self, fields: &[(S::Path, u64)]) -> Vec<S::Path> {
313 fields
314 .iter()
315 .filter(|(path, hash)| {
316 self.last_fields
317 .iter()
318 .find(|(prev_path, _)| prev_path == path)
319 .is_none_or(|(_, prev_hash)| prev_hash != hash)
320 })
321 .map(|(path, _)| *path)
322 .collect()
323 }
324
325 pub fn read_binding(&self) -> ReadStateBinding<S> {
326 ReadStateBinding::new(Arc::clone(&self.state))
327 }
328
329 #[allow(clippy::should_implement_trait)]
330 pub fn next(&mut self) -> Option<ChangeSet<S::Path>> {
331 loop {
332 match self.rx.try_recv() {
333 Ok(()) => {
334 while self.rx.try_recv().is_ok() {}
335 let (root_hash, fields) = self.snapshot_fields();
336 if Some(root_hash) == self.last_hash {
337 continue;
338 }
339 let paths = self.diff_fields(&fields);
340 self.last_hash = Some(root_hash);
341 self.last_fields = fields;
342 if paths.is_empty() {
343 continue;
344 }
345 return Some(ChangeSet { paths });
346 }
347 Err(crossbeam_channel::TryRecvError::Empty) => return None,
348 Err(crossbeam_channel::TryRecvError::Disconnected) => return None,
349 }
350 }
351 }
352
353 pub fn wait_next(&mut self, timeout: Duration) -> Option<ChangeSet<S::Path>> {
354 let deadline = std::time::Instant::now() + timeout;
355 while std::time::Instant::now() < deadline {
356 if let Some(change) = self.next() {
357 return Some(change);
358 }
359 std::thread::sleep(Duration::from_millis(5));
360 }
361 None
362 }
363}
364
365pub struct ScopedRwStore<S: 'static, M: 'static, CS: 'static, CM: 'static, AK: 'static, SK>
367where
368 SK: RefKpTrait<S, CS> + Clone,
369{
370 store: RwStore<S, M>,
371 state_kp: SK,
372 action_kp: AK,
373 _marker: PhantomData<(M, CM, S, CS)>,
374}
375
376impl<S: 'static, M: 'static, CS: 'static, CM: 'static, AK: 'static, SK> Clone
377 for ScopedRwStore<S, M, CS, CM, AK, SK>
378where
379 S: Send,
380 M: Send,
381 AK: Clone,
382 SK: RefKpTrait<S, CS> + Clone,
383{
384 fn clone(&self) -> Self {
385 Self {
386 store: self.store.clone(),
387 state_kp: self.state_kp.clone(),
388 action_kp: self.action_kp.clone(),
389 _marker: PhantomData,
390 }
391 }
392}
393
394impl<S: 'static, M: 'static, CS: 'static, CM: 'static, AK: 'static, SK>
395 ScopedRwStore<S, M, CS, CM, AK, SK>
396where
397 S: Send + Sync + Clone,
398 M: Send,
399 CS: Clone + PartialEq + Send + Sync,
400 CM: Clone + Send,
401 AK: Casepath<M, CM> + Clone + Send + Sync + 'static,
402 SK: RefKpTrait<S, CS> + Clone,
403{
404 pub fn send(&self, action: CM) -> StoreTask {
405 self.store.send(self.action_kp.wrap(action))
406 }
407
408 pub fn dispatch(&self, action: CM) {
409 self.store.dispatch(self.action_kp.wrap(action));
410 }
411
412 pub fn child_state(&self) -> Option<CS> {
413 self.state_kp.focus(&self.store.state()).cloned()
414 }
415
416 pub fn read_binding(&self) -> crate::runtime::binding::ReadProjectedBinding<S, CS, SK> {
417 self.store
418 .read_store()
419 .read_binding()
420 .project(self.state_kp.clone())
421 }
422
423 pub fn rw_binding(&self) -> crate::runtime::binding::RwProjectedBinding<S, CS, SK> {
424 self.store.binding().project(self.state_kp.clone())
425 }
426}