use core::ffi::c_void;
use std::sync::Arc;
use ffi_sdk::BoxedAttachmentHandle;
use_prelude!();
use crate::{
ditto::{DittoHandleWrapper, WeakDittoHandleWrapper},
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(crate) fn new(
token: DittoAttachmentToken,
ditto: DittoHandleWrapper,
on_fetch_event: impl Fn(DittoAttachmentFetchEvent) + Send + Sync + 'a,
) -> Result<Self, DittoError> {
let context = DittoAttachmentFetcherCtx::new(
token.clone(),
Arc::downgrade(&ditto),
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: cancel_token as _,
})
}
}
impl<'a> crate::observer::Observer for DittoAttachmentFetcher<'a> {}
impl<'a> Drop for DittoAttachmentFetcher<'a> {
fn drop(&mut self) {
if let Some(ditto) = self.context.ditto.upgrade() {
let ret = {
ffi_sdk::ditto_cancel_resolve_attachment(
&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: WeakDittoHandleWrapper,
on_fetch_event: Box<dyn Fn(DittoAttachmentFetchEvent) + Send + Sync + 'a>,
}
impl<'a> DittoAttachmentFetcherCtx<'a> {
fn new(
token: DittoAttachmentToken,
ditto: WeakDittoHandleWrapper,
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.clone(),
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);
}
}