dittolive_ditto/store/
observer.rs1use_prelude!();
2
3use std::hash::{self, Hash};
4
5use ffi_sdk::{
6 ffi_utils::repr_c, FfiDynChangeHandlerWithSignalNext, FfiDynSignalNext, FfiQueryResult,
7 FfiStoreObserver,
8};
9use uuid::Uuid;
10
11pub use super::*;
12use crate::{error::DittoError, utils::zstr::zstr};
13
14pub struct StoreObserver {
18 pub(crate) handle: repr_c::Box<FfiStoreObserver>,
19}
20
21trait_alias! {
22 pub trait ChangeHandler =
24 FnMut(QueryResult)
25 + Send + Sync
27 + 'static }
29
30trait_alias! {
31 pub trait SignalNext =
39 FnOnce()
40 + 'static + Send }
43
44trait_alias! {
45 pub trait ChangeHandlerWithSignalNext =
52 FnMut(QueryResult, Box<dyn SignalNext>)
53 + 'static + Send + Sync
56}
57
58fn dittoffi_store_observer_register_safe<F>(
59 ditto: &'_ Ditto,
60 query: &zstr,
61 query_args: Option<&[u8]>,
62 mut observer: F,
63) -> Result<StoreObserver>
64where
65 F: ChangeHandlerWithSignalNext,
66{
67 let ffi_callback: repr_c::Box<FfiDynChangeHandlerWithSignalNext> = Box::new(
68 move |ffi_query_result: repr_c::Box<FfiQueryResult>,
69 signal_next: repr_c::Arc<FfiDynSignalNext>,
70 _: Blocking| {
71 let signal_next = Box::new(move || {
72 #[allow(deprecated)]
73 signal_next.call();
74 });
75
76 observer(QueryResult::from(ffi_query_result), signal_next);
77 },
78 )
79 .into();
80
81 let query = query.into();
82 let query_args_cbor = query_args.map(|qa| qa.into());
83
84 let handle = ffi_sdk::dittoffi_store_register_observer_throws(
85 &ditto.ditto,
86 query,
87 query_args_cbor,
88 ffi_callback,
89 )
90 .into_rust_result()?;
91
92 Ok(StoreObserver { handle })
93}
94
95impl StoreObserver {
96 pub(crate) fn new<F>(
97 ditto: &Ditto,
98 query: &zstr,
99 query_args: Option<&[u8]>,
100 mut on_change: F,
101 ) -> Result<Self, DittoError>
102 where
103 F: ChangeHandler,
104 {
105 Self::with_signal_next(ditto, query, query_args, move |args, signal_next| {
106 on_change(args);
107 signal_next();
108 })
109 }
110
111 pub(crate) fn with_signal_next<F>(
112 ditto: &Ditto,
113 query: &zstr,
114 query_args: Option<&[u8]>,
115 on_change: F,
116 ) -> Result<Self, DittoError>
117 where
118 F: ChangeHandlerWithSignalNext,
119 {
120 let on_change = {
121 let on_change = ::std::sync::Mutex::new(on_change);
122 move |arg: QueryResult, signal_next: Box<dyn SignalNext>| {
123 let mut on_change = on_change
124 .lock()
125 .expect("`on_change` observer not to be poisoned");
126 on_change(arg, signal_next)
127 }
128 };
129 dittoffi_store_observer_register_safe(ditto, query, query_args, on_change)
130 }
131
132 pub fn query_string(&self) -> String {
134 let char_p = ffi_sdk::dittoffi_store_observer_query_string(&self.handle);
135 char_p.into_string()
136 }
137
138 pub fn query_arguments(&self) -> Option<serde_cbor::Value> {
140 let buffer: c_slice::Box<u8> =
141 ffi_sdk::dittoffi_store_observer_query_arguments(&self.handle)?;
142 let cbor = serde_cbor::from_slice(buffer.as_slice())
143 .unwrap_or_else(|error| panic!("bug: failed to deserialize CBOR from FFI: {error}"));
144
145 Some(cbor)
146 }
147
148 pub fn cancel(&self) {
158 ffi_sdk::dittoffi_store_observer_cancel(&self.handle);
159 }
160
161 pub fn is_cancelled(&self) -> bool {
163 ffi_sdk::dittoffi_store_observer_is_cancelled(&self.handle)
164 }
165
166 fn id(&self) -> Uuid {
168 let buffer = ffi_sdk::dittoffi_store_observer_id(&self.handle);
169 Uuid::from_slice(buffer.as_slice()).expect("bug: expected valid UUID")
170 }
171}
172
173impl StoreObserver {
174 fn comparable_parts(&self) -> impl '_ + Eq + Hash {
175 self.id()
176 }
177}
178
179impl Eq for StoreObserver {}
180impl PartialEq for StoreObserver {
181 fn eq(&self, other: &Self) -> bool {
182 self.comparable_parts() == other.comparable_parts()
183 }
184}
185
186impl Hash for StoreObserver {
187 fn hash<H: hash::Hasher>(&self, h: &mut H) {
188 self.comparable_parts().hash(h)
189 }
190}