use std::{
collections::HashMap,
path::{Path, PathBuf},
sync::{Arc, Weak},
};
use ffi_sdk::{BoxedAttachmentHandle, BoxedDitto};
use safer_ffi::prelude::*;
use serde::ser::SerializeMap;
use crate::{ditto::TryUpgrade, prelude::DittoError, utils::prelude::ErrorKind};
mod fetch_event;
pub use self::fetch_event::DittoAttachmentFetchEvent;
mod fetcher;
pub use self::fetcher::{DittoAttachmentFetcher, FetcherVersion};
mod token;
pub use self::token::{DittoAttachmentToken, DittoAttachmentTokenLike};
#[derive(Debug)]
pub struct DittoAttachment {
id: Box<[u8]>,
len: u64,
metadata: HashMap<String, String>,
ditto: Weak<BoxedDitto>,
attachment_handle: BoxedAttachmentHandle,
}
impl DittoAttachment {
pub fn id(&self) -> String {
crate::utils::base64_encode_unpadded(&self.id)
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u64 {
self.len
}
pub fn metadata(&self) -> &HashMap<String, String> {
&self.metadata
}
}
impl serde::Serialize for DittoAttachment {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(Some(4))?;
map.serialize_entry(
"_ditto_internal_type_jkb12973t4b",
&(::ffi_sdk::DittoCrdtType::Attachment as u64),
)?;
map.serialize_entry("_id", ::serde_bytes::Bytes::new(&self.id[..]))?;
map.serialize_entry("_len", &self.len)?;
map.serialize_entry("_meta", &self.metadata)?;
map.end()
}
}
impl DittoAttachment {
pub(crate) fn new(
id: Box<[u8]>,
len: u64,
metadata: HashMap<String, String>,
ditto: Weak<BoxedDitto>,
attachment_handle: BoxedAttachmentHandle,
) -> Self {
Self {
id,
len,
metadata,
ditto,
attachment_handle,
}
}
pub(crate) fn from_file_and_metadata(
filepath: &(impl ?Sized + AsRef<Path>),
metadata: HashMap<String, String>,
ditto: &Arc<ffi_sdk::BoxedDitto>,
) -> Result<DittoAttachment, DittoError> {
let source_path = char_p::new(filepath.as_ref().to_str().unwrap());
let file_operation = ffi_sdk::AttachmentFileOperation::Copy;
let mut slot = ::core::mem::MaybeUninit::<ffi_sdk::Attachment>::uninit();
let status = {
ffi_sdk::ditto_new_attachment_from_file(
ditto,
source_path.as_ref(),
file_operation,
slot.as_out(),
)
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
let attachment = unsafe { slot.assume_init() }; let ret = DittoAttachment::new(
attachment.id.into(),
attachment.len,
metadata,
Arc::downgrade(ditto),
attachment.handle,
);
Ok(ret)
}
}
pub(crate) fn from_bytes_and_metadata(
bytes: &(impl ?Sized + AsRef<[u8]>),
metadata: HashMap<String, String>,
ditto: &Arc<ffi_sdk::BoxedDitto>,
) -> Result<DittoAttachment, DittoError> {
let mut slot = ::core::mem::MaybeUninit::<ffi_sdk::Attachment>::uninit();
let status = {
ffi_sdk::ditto_new_attachment_from_bytes(ditto, bytes.as_ref().into(), slot.as_out())
};
if status != 0 {
Err(DittoError::from_ffi(ErrorKind::InvalidInput))
} else {
let attachment = unsafe { slot.assume_init() }; let ret = DittoAttachment::new(
attachment.id.into(),
attachment.len,
metadata,
Arc::downgrade(ditto),
attachment.handle,
);
Ok(ret)
}
}
pub(crate) fn new_with_token(
token: DittoAttachmentToken,
ditto: Weak<BoxedDitto>,
attachment_handle: BoxedAttachmentHandle,
) -> Self {
Self {
id: token.id,
len: token.len,
metadata: token.metadata,
ditto,
attachment_handle,
}
}
pub fn path(&self) -> PathBuf {
let ditto = self.ditto.try_upgrade().unwrap();
let p = ffi_sdk::ditto_get_complete_attachment_path(&ditto, &self.attachment_handle);
let p_string = p.to_string();
p_string.into()
}
}