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 core::ffi::c_void;
use std::sync::Arc;

use ffi_sdk::{BoxedAttachmentHandle, BoxedDitto};
use_prelude!();

use crate::{
    error::DittoError,
    store::{
        ditto_attachment::DittoAttachment, ditto_attachment_fetch_event::DittoAttachmentFetchEvent,
        ditto_attachment_token::DittoAttachmentToken,
    },
};

pub struct DittoAttachmentFetcher<'a> {
    context: Arc<DittoAttachmentFetcherCtx<'a>>,
    cancel_token: u64,
}

impl<'a> DittoAttachmentFetcher<'a> {
    pub fn new(
        token: DittoAttachmentToken,
        ditto: Arc<BoxedDitto>,
        on_fetch_event: impl Fn(DittoAttachmentFetchEvent) + Send + Sync + 'a,
    ) -> Result<Self, DittoError> {
        let context =
            DittoAttachmentFetcherCtx::new(token.clone(), ditto.retain(), Box::new(on_fetch_event));
        let arc_context = Arc::new(context);
        let raw_context = Arc::as_ptr(&arc_context) as *mut c_void;

        let cancel_token = unsafe {
            ffi_sdk::ditto_resolve_attachment(
                &ditto,
                token.id.as_ref().into(),
                raw_context,
                Some(DittoAttachmentFetcherCtx::retain),
                Some(DittoAttachmentFetcherCtx::release),
                DittoAttachmentFetcherCtx::on_complete_cb,
                DittoAttachmentFetcherCtx::on_progress_cb,
                DittoAttachmentFetcherCtx::on_deleted_cb,
            )
            .ok()?
        };

        Ok(Self {
            context: arc_context,
            cancel_token,
        })
    }
}

impl<'a> crate::observer::Observer for DittoAttachmentFetcher<'a> {}

impl<'a> Drop for DittoAttachmentFetcher<'a> {
    fn drop(&mut self) {
        let ret = unsafe {
            ffi_sdk::ditto_cancel_resolve_attachment(
                &self.context.ditto,
                self.context.token.id.as_ref().into(),
                self.cancel_token,
            )
        };

        if ret != 0 {
            log::warn!("Failed to clean up attachment fetcher.",);
        }
    }
}

struct DittoAttachmentFetcherCtx<'a> {
    token: DittoAttachmentToken,
    ditto: Arc<BoxedDitto>,
    on_fetch_event: Box<dyn Fn(DittoAttachmentFetchEvent) + Send + Sync + 'a>,
}

impl<'a> DittoAttachmentFetcherCtx<'a> {
    fn new(
        token: DittoAttachmentToken,
        ditto: Arc<BoxedDitto>,
        on_fetch_event: Box<dyn Fn(DittoAttachmentFetchEvent) + Send + Sync + 'a>,
    ) -> Self {
        Self {
            token,
            ditto,
            on_fetch_event,
        }
    }

    pub(crate) unsafe extern "C" fn retain(ctx: *mut c_void) {
        let ptr = ctx.cast::<DittoAttachmentFetcherCtx>();
        Arc::increment_strong_count(ptr);
    }

    pub(crate) unsafe extern "C" fn release(ctx: *mut c_void) {
        let ptr = ctx.cast::<DittoAttachmentFetcherCtx>();
        Arc::decrement_strong_count(ptr);
    }

    pub(crate) unsafe extern "C" fn on_complete_cb(
        ctx: *mut c_void,
        attachment_handle: BoxedAttachmentHandle,
    ) {
        let ctx_ref = ctx
            .cast::<DittoAttachmentFetcherCtx>()
            .as_ref()
            .expect("got null");

        let ditto_attachment = DittoAttachment::new_with_token(
            ctx_ref.token.clone(),
            ctx_ref.ditto.retain(),
            attachment_handle,
        );
        let event = DittoAttachmentFetchEvent::Completed {
            attachment: ditto_attachment,
        };
        (ctx_ref.on_fetch_event)(event);
    }

    pub(crate) unsafe extern "C" fn on_progress_cb(
        ctx: *mut c_void,
        downloaded_bytes: u64,
        total_bytes: u64,
    ) {
        let ctx_ref = ctx
            .cast::<DittoAttachmentFetcherCtx>()
            .as_ref()
            .expect("got null");

        let event = DittoAttachmentFetchEvent::Progress {
            downloaded_bytes,
            total_bytes,
        };
        (ctx_ref.on_fetch_event)(event);
    }

    pub(crate) unsafe extern "C" fn on_deleted_cb(ctx: *mut c_void) {
        let ctx_ref = ctx
            .cast::<DittoAttachmentFetcherCtx>()
            .as_ref()
            .expect("got null");

        let event = DittoAttachmentFetchEvent::Deleted;
        (ctx_ref.on_fetch_event)(event);
    }
}