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