use std::{collections::HashMap, path::PathBuf};
use ffi_sdk::BoxedAttachmentHandle;
use serde::ser::SerializeMap;
use super::ditto_attachment_token::DittoAttachmentToken;
use crate::ditto::{TryUpgrade, WeakDittoHandleWrapper};
#[derive(Debug)]
pub struct DittoAttachment {
id: Box<[u8]>,
len: u64,
metadata: HashMap<String, String>,
ditto: WeakDittoHandleWrapper,
attachment_handle: BoxedAttachmentHandle,
}
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: WeakDittoHandleWrapper,
attachment_handle: BoxedAttachmentHandle,
) -> Self {
Self {
id,
len,
metadata,
ditto,
attachment_handle,
}
}
pub(crate) fn new_with_token(
token: DittoAttachmentToken,
ditto: WeakDittoHandleWrapper,
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()
}
}
#[cfg(test)]
mod tests {
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex,
},
};
use serde_json::json;
use crate::{
prelude::*,
store::{
ditto_attachment_fetch_event::DittoAttachmentFetchEvent,
ditto_attachment_token::DittoAttachmentToken,
},
test_helpers::setup_ditto,
};
#[test]
fn attachment_serialize() {
let ditto = setup_ditto().unwrap();
let store = ditto.store();
let collection = store.collection("test").unwrap();
let original_test_file_path = "tests/data/attachment_file_1.txt";
let metadata = {
let mut m = HashMap::new();
m.insert("key_1".to_string(), "value_1".to_string());
m.insert("key_2".to_string(), "value_2".to_string());
m
};
let attachment = collection
.new_attachment(original_test_file_path, metadata.clone())
.expect("new_attachment");
let attachment_id = attachment.id.clone();
let attachment_len = attachment.len;
let attachment_file_path = attachment.path();
assert_ne!(
original_test_file_path,
attachment_file_path
.clone()
.into_os_string()
.into_string()
.unwrap()
);
let collection = store.collection("test").unwrap();
let id = collection
.upsert(json!({ "hello": "again", "att": attachment }))
.unwrap();
{
let mut map = HashMap::new();
map.insert("hello", serde_cbor::value::to_value("again").unwrap());
map.insert("att", serde_cbor::value::to_value(&attachment).unwrap());
let _other = collection.upsert(map).unwrap();
}
let mut doc = collection.find_by_id(id).exec().unwrap();
let set = doc.set("att_two", attachment);
assert!(set.is_ok());
let attachment_token = doc.get::<DittoAttachmentToken>("att").unwrap();
assert_eq!(attachment_token.id, attachment_id);
assert_eq!(attachment_token.len, attachment_len);
assert_eq!(attachment_token.metadata, metadata);
let attachment_token_two = doc.get::<DittoAttachmentToken>("att_two").unwrap();
assert_eq!(attachment_token.id, attachment_token_two.id);
assert_eq!(attachment_token.len, attachment_token_two.len);
assert_eq!(attachment_token.metadata, attachment_token_two.metadata);
let test_file = std::fs::read(original_test_file_path).unwrap();
let attachment_file = std::fs::read(attachment_file_path).unwrap();
assert_eq!(test_file, attachment_file);
assert_eq!(test_file.len() as u64, attachment_len);
}
#[test]
fn attachment_fetch() {
let ditto = setup_ditto().unwrap();
let store = ditto.store();
let collection = store.collection("test").unwrap();
let original_test_file_path = "tests/data/attachment_file_1.txt";
let attachment = collection
.new_attachment(original_test_file_path, HashMap::new())
.expect("new_attachment");
let collection = store.collection("test").unwrap();
let id = collection.upsert(json!({"hello": "again"})).unwrap();
let mut doc = collection.find_by_id(id).exec().unwrap();
let set = doc.set("att", attachment);
assert!(set.is_ok());
let attachment_token = doc.get::<DittoAttachmentToken>("att").unwrap();
let finished = Arc::new(AtomicBool::new(false));
let finished_clone = Arc::clone(&finished);
let fetched_attachment_data: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(vec![]));
let _fetcher = collection
.fetch_attachment(attachment_token, |event| {
if let DittoAttachmentFetchEvent::Completed { attachment } = event {
let att_data_mtx = &*fetched_attachment_data; if let Ok(mut fetched_attachment_data) = att_data_mtx.lock() {
*fetched_attachment_data = std::fs::read(attachment.path()).unwrap();
finished_clone.store(true, Ordering::SeqCst);
}
}
})
.unwrap();
while !finished.load(Ordering::SeqCst) {
std::thread::yield_now();
}
let test_file_data = std::fs::read(original_test_file_path).unwrap();
let fetched_att_data = fetched_attachment_data.lock().unwrap();
assert_eq!(test_file_data, *fetched_att_data);
}
}