dittolive-ditto 3.0.0-alpha2

Ditto is a peer to peer cross-platform database that allows mobile, web, IoT and server apps to sync with or without an internet connection.
Documentation
use_prelude!();

use ffi_sdk::COrderByParam;

use super::event::CollectionsEvent;
use crate::{
    error::DittoError, store::collection::pending_cursor_operation::*, subscription::Subscription,
};

trait_alias! {
    /// A closure which is called on each event relating to changes in the known
    /// about collections.
    ///
    /// Ditto may call this handler in parallel such as when a second invocation
    /// happens before the first invocation has completed. This typically occurs
    /// when the closure makes slow, external calls.
    pub
    trait CollectionsEventHandler =
        FnMut(CollectionsEvent) // callable sequentially
        + Send // can be dropped in another thread
        + 'static // cannot dangle
}

pub struct PendingCollectionsOperation<'order_by> {
    pub(super) ditto: Arc<BoxedDitto>,
    pub(super) pending_cursor_operation: PendingCursorOperation<'order_by>,
}

impl<'order_by> PendingCollectionsOperation<'order_by> {
    pub fn new(ditto: Arc<BoxedDitto>) -> Self {
        let pending_cursor_operation =
            PendingCursorOperation::new(ditto.retain(), char_p::new("__collections"), "true", None);
        Self {
            ditto,
            pending_cursor_operation,
        }
    }

    /// Limit the number of documents that get returned when querying a
    /// collection for matching documents.
    pub fn limit(&mut self, limit: i32) -> &mut Self {
        self.pending_cursor_operation.limit(limit);
        self
    }

    /// Offset the resulting set of matching documents. This is useful if you
    /// aren’t interested in the first N matching documents for one reason
    /// or another. For example, you might already have queried the
    /// collection and obtained the first 20 matching documents and so you might
    /// want to run the same query as you did previously but ignore the first 20
    /// matching documents, and that is where you would use offset.
    pub fn offset(&mut self, offset: u32) -> &mut Self {
        self.pending_cursor_operation.offset(offset);
        self
    }

    // FIXME: To bring this in line with the other SDKs this should accept a single
    // "order_by" expression, which should then be added to the `order_by` vec
    /// Sort the documents that match the query provided in the preceding
    /// find-like function call.
    pub fn sort(&mut self, sort_param: Vec<COrderByParam<'order_by>>) -> &mut Self {
        self.pending_cursor_operation.sort(sort_param);
        self
    }

    /// Execute the query generated by the preceding function chaining and
    /// return the list of matching documents. This occurs immediately.
    pub fn exec(&self) -> Result<Vec<Collection>, DittoError> {
        let docs = self.pending_cursor_operation.exec()?;
        Ok(PendingCollectionsOperation::collections_from_docs(
            docs,
            &self.ditto,
        ))
    }

    /// Enables you to subscribe to changes that occur on a collection. Having a
    /// subscription acts as a signal to others that you are interested in
    /// receiving updates when local or remote changes are made to documents
    /// that match the query generated by the chain of operations
    /// that precedes the call to subscribe. The returned DittoSubscription
    /// object must be kept in scope for as long as you want to keep
    /// receiving updates.
    pub fn subscribe(&self) -> Subscription {
        self.pending_cursor_operation.subscribe()
    }

    /// Enables you to listen for changes that occur on a collection. This won’t
    /// subscribe to receive changes made remotely by others and so it will only
    /// fire updates when a local change is made. If you want to receive
    /// remotely performed updates as well then also call subscribe with the
    /// relevant query. The returned DittoLiveQuery object must be kept in scope
    /// for as long as you want the provided eventHandler to be called when an
    /// update occurs.
    pub fn observe_local<Handler>(&self, handler: Handler) -> Result<LiveQuery, DittoError>
    where
        Handler: CollectionsEventHandler,
    {
        let ditto = self.ditto.retain();
        let mut handler = handler;
        let handler = move |docs: Vec<BoxedDocument>, event: LiveQueryEvent| {
            let colls_ev = PendingCollectionsOperation::collections_event_from_docs_and_event(
                docs, event, &ditto,
            );
            handler(colls_ev);
        };
        self.pending_cursor_operation.observe_local(handler)
    }

    fn collections_from_docs(docs: Vec<BoxedDocument>, ditto: &Arc<BoxedDitto>) -> Vec<Collection> {
        docs.iter()
            .filter_map(|doc| {
                doc.get::<String>("name")
                    .ok()
                    .map(|coll_name| Collection::new(ditto.retain(), coll_name))
            })
            .collect()
    }

    fn collections_event_from_docs_and_event(
        docs: Vec<BoxedDocument>,
        event: LiveQueryEvent,
        ditto: &Arc<BoxedDitto>,
    ) -> CollectionsEvent {
        match event {
            LiveQueryEvent::Initial => CollectionsEvent::initial(
                PendingCollectionsOperation::collections_from_docs(docs, ditto),
            ),
            LiveQueryEvent::Update {
                old_documents,
                insertions,
                deletions,
                updates,
                moves,
            } => CollectionsEvent::update(
                PendingCollectionsOperation::collections_from_docs(docs, ditto),
                PendingCollectionsOperation::collections_from_docs(old_documents, ditto),
                insertions,
                deletions,
                updates,
                moves,
            ),
        }
    }
}