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}