dittolive_ditto/store/attachment/
mod.rs1use std::{
4 collections::HashMap,
5 path::{Path, PathBuf},
6 sync::{Arc, Weak},
7};
8
9use ffi_sdk::{BoxedAttachmentHandle, BoxedDitto};
10use safer_ffi::prelude::*;
11use serde::ser::SerializeMap;
12
13use crate::{ditto::TryUpgrade, prelude::DittoError, utils::prelude::ErrorKind};
14
15mod fetch_event;
16pub use self::fetch_event::DittoAttachmentFetchEvent;
17
18mod fetcher;
19pub use self::fetcher::{DittoAttachmentFetcher, FetcherVersion};
20
21mod token;
22pub use self::token::{DittoAttachmentToken, DittoAttachmentTokenLike};
23
24#[derive(Debug)]
27pub struct DittoAttachment {
28 id: Box<[u8]>,
29 len: u64,
30 metadata: HashMap<String, String>,
31 ditto: Weak<BoxedDitto>,
32 attachment_handle: BoxedAttachmentHandle,
33}
34
35impl DittoAttachment {
36 pub fn id(&self) -> String {
38 crate::utils::base64_encode_unpadded(&self.id)
39 }
40
41 #[allow(clippy::len_without_is_empty)]
43 pub fn len(&self) -> u64 {
44 self.len
45 }
46
47 pub fn metadata(&self) -> &HashMap<String, String> {
50 &self.metadata
51 }
52}
53
54impl serde::Serialize for DittoAttachment {
55 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
56 where
57 S: serde::Serializer,
58 {
59 let mut map = serializer.serialize_map(Some(4))?;
60 map.serialize_entry(
61 "_ditto_internal_type_jkb12973t4b",
65 &(::ffi_sdk::DittoCrdtType::Attachment as u64),
66 )?;
67 map.serialize_entry("_id", ::serde_bytes::Bytes::new(&self.id[..]))?;
68 map.serialize_entry("_len", &self.len)?;
69 map.serialize_entry("_meta", &self.metadata)?;
70 map.end()
71 }
72}
73
74impl DittoAttachment {
75 pub(crate) fn new(
77 id: Box<[u8]>,
78 len: u64,
79 metadata: HashMap<String, String>,
80 ditto: Weak<BoxedDitto>,
81 attachment_handle: BoxedAttachmentHandle,
82 ) -> Self {
83 Self {
84 id,
85 len,
86 metadata,
87 ditto,
88 attachment_handle,
89 }
90 }
91
92 pub(crate) fn from_file_and_metadata(
93 filepath: &(impl ?Sized + AsRef<Path>),
94 metadata: HashMap<String, String>,
95 ditto: &Arc<ffi_sdk::BoxedDitto>,
96 ) -> Result<DittoAttachment, DittoError> {
97 let source_path = char_p::new(filepath.as_ref().to_str().unwrap());
98 let file_operation = ffi_sdk::AttachmentFileOperation::Copy;
99 let mut slot = ::core::mem::MaybeUninit::<ffi_sdk::Attachment>::uninit();
100 let status = {
101 ffi_sdk::ditto_new_attachment_from_file(
102 ditto,
103 source_path.as_ref(),
104 file_operation,
105 slot.as_out(),
106 )
107 };
108 if status != 0 {
109 Err(DittoError::from_ffi(ErrorKind::InvalidInput))
110 } else {
111 let attachment = unsafe { slot.assume_init() }; let ret = DittoAttachment::new(
113 attachment.id.into(),
114 attachment.len,
115 metadata,
116 Arc::downgrade(ditto),
117 attachment.handle,
118 );
119 Ok(ret)
120 }
121 }
122
123 pub(crate) fn from_bytes_and_metadata(
124 bytes: &(impl ?Sized + AsRef<[u8]>),
125 metadata: HashMap<String, String>,
126 ditto: &Arc<ffi_sdk::BoxedDitto>,
127 ) -> Result<DittoAttachment, DittoError> {
128 let mut slot = ::core::mem::MaybeUninit::<ffi_sdk::Attachment>::uninit();
129 let status = {
130 ffi_sdk::ditto_new_attachment_from_bytes(ditto, bytes.as_ref().into(), slot.as_out())
131 };
132 if status != 0 {
133 Err(DittoError::from_ffi(ErrorKind::InvalidInput))
134 } else {
135 let attachment = unsafe { slot.assume_init() }; let ret = DittoAttachment::new(
137 attachment.id.into(),
138 attachment.len,
139 metadata,
140 Arc::downgrade(ditto),
141 attachment.handle,
142 );
143 Ok(ret)
144 }
145 }
146
147 pub(crate) fn new_with_token(
149 token: DittoAttachmentToken,
150 ditto: Weak<BoxedDitto>,
151 attachment_handle: BoxedAttachmentHandle,
152 ) -> Self {
153 Self {
154 id: token.id,
155 len: token.len,
156 metadata: token.metadata,
157 ditto,
158 attachment_handle,
159 }
160 }
161
162 pub fn path(&self) -> PathBuf {
166 let ditto = self.ditto.try_upgrade().unwrap();
168 let p = ffi_sdk::ditto_get_complete_attachment_path(&ditto, &self.attachment_handle);
169 let p_string = p.to_string();
170 p_string.into()
171 }
172}