dittolive_ditto/sync/mod.rs
1//! Use [`ditto.sync()`] to access the [`Sync`] API of Ditto.
2//!
3//! The [`Sync`] API can be used to request documents to be synchronized with
4//! other peers. Use [`ditto.sync().register_subscription(...)`] and provide
5//! a DQL query to let Ditto know which documents you're interested in syncing.
6//!
7//! The returned [`SyncSubscription`] handle can be used to cancel the
8//! subscription with [`.cancel()`], at which point Ditto will stop syncing
9//! data for that subscription.
10//!
11//! # Example
12//!
13//! ```
14//! use dittolive_ditto::prelude::*;
15//! # fn example(ditto: &Ditto) -> anyhow::Result<()> {
16//!
17//! let sync_subscription = ditto
18//! .sync()
19//! .register_subscription("SELECT * FROM cars WHERE color = 'blue'", None)?;
20//!
21//! // To cancel the sync subscription, use .cancel()
22//! sync_subscription.cancel();
23//! # Ok(())
24//! # }
25//! ```
26//!
27//! [`ditto.sync()`]: Ditto::sync
28//! [`ditto.sync().register_subscription(...)`]: Sync::register_subscription
29//! [`.cancel()`]: SyncSubscription::cancel
30
31use std::{
32 collections::HashSet,
33 ops::Deref,
34 sync::{Arc, Weak},
35};
36
37use serde::Serialize;
38
39pub use self::sync_subscription::SyncSubscription;
40
41mod sync_subscription;
42
43use crate::{
44 ditto::{Ditto, DittoFields},
45 dql::{query_v2::IntoQuery, *},
46 error::{DittoError, ErrorKind},
47 utils::SetArc,
48};
49
50/// Ditto's `Sync` API, obtained via [`ditto.sync()`].
51///
52/// [See the `sync` module documentation for more details][0].
53///
54/// [`ditto.sync()`]: crate::prelude::Ditto::sync
55/// [0]: crate::sync
56pub struct Sync {
57 ditto: Weak<DittoFields>,
58}
59
60impl Sync {
61 pub(crate) fn new(ditto: Weak<DittoFields>) -> Self {
62 Self { ditto }
63 }
64
65 /// Starts the network transports. Ditto will connect to other devices.
66 ///
67 /// By default, Ditto will enable all peer-to-peer transport types.
68 /// The network configuration can be customized using
69 /// [`ditto.set_transport_config()`].
70 ///
71 /// [`ditto.set_transport_config()`]: crate::prelude::Ditto::set_transport_config
72 pub fn start(&self) -> Result<(), DittoError> {
73 let ditto = Ditto::upgrade(&self.ditto)?;
74 let result = ffi_sdk::dittoffi_ditto_try_start_sync(&ditto.ditto);
75 if let Some(error) = result.error {
76 if ffi_sdk::dittoffi_error_code(&*error)
77 == ffi_sdk::FfiErrorCode::ActivationNotActivated
78 {
79 return Err(ErrorKind::NotActivated.into());
80 }
81 return Err(DittoError::from(error));
82 }
83 Ok(())
84 }
85
86 /// Stops syncing on all transports.
87 ///
88 /// You may continue to use the Ditto store locally but no data will sync
89 /// to or from other devices.
90 pub fn stop(&self) {
91 if let Ok(ditto) = Ditto::upgrade(&self.ditto) {
92 ffi_sdk::dittoffi_ditto_stop_sync(&ditto.ditto);
93 }
94 }
95
96 /// Returns `true` if sync is currently active, otherwise returns `false`.
97 pub fn is_active(&self) -> bool {
98 match Ditto::upgrade(&self.ditto) {
99 Ok(ditto) => ffi_sdk::dittoffi_ditto_is_sync_active(&ditto.ditto),
100 Err(_) => false,
101 }
102 }
103
104 /// Returns a snapshot of handles to all active [`SyncSubscription`]s.
105 ///
106 /// Adding or removing [`SyncSubscription`]s from this map will not cause the
107 /// underlying subscriptions to be updated.
108 ///
109 /// # Example
110 ///
111 /// ```
112 /// # use dittolive_ditto::Ditto;
113 /// # fn example(ditto: &Ditto) -> anyhow::Result<()> {
114 /// let subscription = ditto
115 /// .sync()
116 /// .register_subscription("SELECT * FROM cars", None)?;
117 /// let subscriptions = ditto.sync().subscriptions();
118 /// assert!(subscriptions.contains(&subscription));
119 /// # Ok(())
120 /// # }
121 /// ```
122 #[doc(hidden)]
123 #[deprecated(note = "Use `ditto.sync().subscriptions_v2()` instead.")]
124 pub fn subscriptions(&self) -> impl '_ + Deref<Target = SetArc<SyncSubscription>> {
125 let ditto = Ditto::upgrade(&self.ditto).expect("Ditto went out of scope");
126 let sync_subscriptions = ffi_sdk::dittoffi_sync_subscriptions(&ditto.ditto);
127 let sync_subscriptions: Vec<_> = sync_subscriptions.into();
128
129 let sync_subscriptions = sync_subscriptions
130 .into_iter()
131 .map(|handle| Arc::new(SyncSubscription { handle }))
132 .collect::<SetArc<_>>();
133
134 Box::new(sync_subscriptions)
135 }
136
137 /// Returns a snapshot of handles to all active [`SyncSubscription`]s.
138 ///
139 /// Adding or removing [`SyncSubscription`]s from this map will not cause the
140 /// underlying subscriptions to be updated.
141 ///
142 /// # Example
143 ///
144 /// ```
145 /// # use dittolive_ditto::Ditto;
146 /// # fn example(ditto: &Ditto) -> anyhow::Result<()> {
147 /// let subscription = ditto
148 /// .sync()
149 /// .register_subscription("SELECT * FROM cars", None)?;
150 /// let subscriptions = ditto.sync().subscriptions_v2();
151 /// assert!(subscriptions.contains(&subscription));
152 /// # Ok(())
153 /// # }
154 /// ```
155 pub fn subscriptions_v2(&self) -> HashSet<SyncSubscription> {
156 let ditto = Ditto::upgrade(&self.ditto).expect("Ditto went out of scope");
157 let sync_subscriptions = ffi_sdk::dittoffi_sync_subscriptions(&ditto.ditto);
158 let sync_subscriptions: Vec<_> = sync_subscriptions.into();
159
160 sync_subscriptions
161 .into_iter()
162 .map(|handle| SyncSubscription { handle })
163 .collect::<HashSet<_>>()
164 }
165
166 /// Deprecated in favour of
167 /// [`ditto.sync().register_subscription_v2()`][Sync::register_subscription_v2]
168 #[deprecated = "Use `ditto.sync().register_subscription_v2(...) instead"]
169 #[allow(deprecated)]
170 #[doc(hidden)]
171 pub fn register_subscription<Q>(
172 &self,
173 query: Q,
174 query_args: Option<QueryArguments>,
175 ) -> Result<Arc<SyncSubscription>, DittoError>
176 where
177 Q: TryInto<Query, Error = DittoError>,
178 {
179 let ditto = Ditto::upgrade(&self.ditto)?;
180 let query: Query = query.try_into()?;
181 let query_args = query_args.as_ref().map(|a| a.cbor());
182 let subscription = SyncSubscription::new(&ditto, &query.inner_string, query_args)?;
183 let subscription = Arc::new(subscription);
184 Ok(subscription)
185 }
186
187 /// Use a DQL query to subscribe to data on other Ditto peers.
188 ///
189 /// While the subscription is active, data matching this query on other
190 /// peers will be synced to the local peer's data store.
191 ///
192 /// Note that dropping the `SyncSubscription` won't cancel it, to do that
193 /// be sure to use [`sync_subscription.cancel()`].
194 ///
195 /// # Example
196 ///
197 /// ```
198 /// use dittolive_ditto::prelude::*;
199 /// # fn example(ditto: &Ditto) -> anyhow::Result<()> {
200 /// let query = (
201 /// "SELECT * FROM cars WHERE color = :color",
202 /// serde_json::json!({"color": "blue"}),
203 /// );
204 ///
205 /// let sync_subscription = ditto.sync().register_subscription_v2(query)?;
206 ///
207 /// // Cancel the subscription with `.cancel()`
208 /// sync_subscription.cancel();
209 /// # Ok(())
210 /// # }
211 /// ```
212 ///
213 /// [`sync_subscription.cancel()`]: crate::sync::SyncSubscription::cancel
214 pub fn register_subscription_v2<Q>(&self, query: Q) -> Result<Arc<SyncSubscription>, DittoError>
215 where
216 Q: IntoQuery,
217 Q::Args: Serialize,
218 {
219 let ditto = Ditto::upgrade(&self.ditto)?;
220 let query = query.into_query()?;
221 let subscription =
222 SyncSubscription::new(&ditto, &query.string, query.args_cbor.as_deref())?;
223 let subscription = Arc::new(subscription);
224 Ok(subscription)
225 }
226}