dittolive_ditto/store/
observer.rs1use_prelude!();
2
3use std::hash::{self, Hash};
4
5use ffi_sdk::{
6 ffi_utils::repr_c, FfiQueryResult, FfiSignalNext, FfiStoreObservationHandlerWithSignalNext,
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 =
40 FnOnce()
41 + 'static + Send }
44
45trait_alias! {
46 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: FfiStoreObservationHandlerWithSignalNext = {
68 fn make_callback<F>(f: F) -> FfiStoreObservationHandlerWithSignalNext
69 where
70 F: FnMut(repr_c::Box<FfiQueryResult>, repr_c::Arc<FfiSignalNext>) + 'static + Send,
71 {
72 FfiStoreObservationHandlerWithSignalNext(Box::new(f).into())
73 }
74
75 make_callback(
76 move |ffi_query_result: repr_c::Box<FfiQueryResult>,
77 signal_next: repr_c::Arc<FfiSignalNext>| {
78 let signal_next = Box::new(move || {
79 signal_next.call();
80 });
81
82 observer(QueryResult::from(ffi_query_result), signal_next);
83 },
84 )
85 };
86
87 let query = query.into();
88 let query_args_cbor = query_args.map(|qa| qa.into());
89
90 let handle = ffi_sdk::dittoffi_store_register_observer_throws(
91 &ditto.ditto,
92 query,
93 query_args_cbor,
94 ffi_callback,
95 )
96 .into_rust_result()?;
97
98 Ok(StoreObserver { handle })
99}
100
101impl StoreObserver {
102 pub(crate) fn new<F>(
103 ditto: &Ditto,
104 query: &zstr,
105 query_args: Option<&[u8]>,
106 mut on_change: F,
107 ) -> Result<Self, DittoError>
108 where
109 F: ChangeHandler,
110 {
111 Self::with_signal_next(ditto, query, query_args, move |args, signal_next| {
112 on_change(args);
113 signal_next();
114 })
115 }
116
117 pub(crate) fn with_signal_next<F>(
118 ditto: &Ditto,
119 query: &zstr,
120 query_args: Option<&[u8]>,
121 on_change: F,
122 ) -> Result<Self, DittoError>
123 where
124 F: ChangeHandlerWithSignalNext,
125 {
126 let on_change = {
127 let on_change = ::std::sync::Mutex::new(on_change);
128 move |arg: QueryResult, signal_next: Box<dyn SignalNext>| {
129 let mut on_change = on_change
130 .lock()
131 .expect("`on_change` observer not to be poisoned");
132 on_change(arg, signal_next)
133 }
134 };
135 dittoffi_store_observer_register_safe(ditto, query, query_args, on_change)
136 }
137
138 pub fn query_string(&self) -> String {
140 let char_p = ffi_sdk::dittoffi_store_observer_query_string(&self.handle);
141 char_p.into_string()
142 }
143
144 pub fn query_arguments(&self) -> Option<serde_cbor::Value> {
146 let buffer: c_slice::Box<u8> =
147 ffi_sdk::dittoffi_store_observer_query_arguments_cbor(&self.handle)?;
148 let cbor = serde_cbor::from_slice(buffer.as_slice())
149 .unwrap_or_else(|error| panic!("bug: failed to deserialize CBOR from FFI: {error}"));
150
151 Some(cbor)
152 }
153
154 pub fn cancel(&self) {
164 ffi_sdk::dittoffi_store_observer_cancel(&self.handle);
165 }
166
167 pub fn is_cancelled(&self) -> bool {
169 ffi_sdk::dittoffi_store_observer_is_cancelled(&self.handle)
170 }
171
172 fn id(&self) -> Uuid {
174 let buffer = ffi_sdk::dittoffi_store_observer_id(&self.handle);
175 Uuid::from_slice(buffer.as_slice()).expect("bug: expected valid UUID")
176 }
177}
178
179impl StoreObserver {
180 fn comparable_parts(&self) -> impl '_ + Eq + Hash {
181 self.id()
182 }
183}
184
185impl Eq for StoreObserver {}
186impl PartialEq for StoreObserver {
187 fn eq(&self, other: &Self) -> bool {
188 self.comparable_parts() == other.comparable_parts()
189 }
190}
191
192impl Hash for StoreObserver {
193 fn hash<H: hash::Hasher>(&self, h: &mut H) {
194 self.comparable_parts().hash(h)
195 }
196}