dittolive_ditto/store/attachment/
token.rs

1use std::{
2    collections::{BTreeMap, HashMap},
3    convert::TryFrom,
4};
5
6use serde::Deserialize;
7
8use crate::{
9    error::{DittoError, ErrorKind},
10    prelude::CborValue,
11};
12
13#[derive(Debug, Clone, Deserialize)]
14#[serde(try_from = "DittoAttachmentTokenStoredFormat")]
15/// Serves as a token for a specific attachment that you can pass to a call to
16/// [`fetch_attachment`](crate::store::collection::Collection::fetch_attachment).
17pub struct DittoAttachmentToken {
18    pub(crate) id: Box<[u8]>,
19    pub(crate) len: u64,
20    pub(crate) metadata: HashMap<String, String>,
21}
22
23impl DittoAttachmentToken {
24    /// Returns the `id` of this attachment, encoded so as to be compatible with the
25    /// [`crate::store::dql::Query`] API.
26    pub fn id(&self) -> String {
27        crate::utils::base64_encode_unpadded(&self.id)
28    }
29
30    /// Returns the size, in bytes, of this attachment's data. Compatible with the
31    /// [`crate::store::dql::Query`] API.
32    #[allow(clippy::len_without_is_empty)]
33    pub fn len(&self) -> u64 {
34        self.len
35    }
36
37    /// Returns the metadata that was associated with this attachment file when the source peer
38    /// called [`new_attachment`][crate::store::Store::new_attachment].
39    pub fn metadata(&self) -> &HashMap<String, String> {
40        &self.metadata
41    }
42}
43
44impl TryFrom<DittoAttachmentTokenStoredFormat> for DittoAttachmentToken {
45    type Error = DittoError;
46
47    fn try_from(value: DittoAttachmentTokenStoredFormat) -> Result<Self, Self::Error> {
48        if value._type != (::ffi_sdk::DittoCrdtType::Attachment as u64) {
49            return Err(ErrorKind::Internal.into());
50        }
51        let ret = DittoAttachmentToken {
52            id: value._id,
53            len: value._len,
54            metadata: value._meta,
55        };
56        Ok(ret)
57    }
58}
59
60// Helper struct to help deserialize. This is to let us use
61// #[derive(Deserialize)] instead of manually implementing it.
62// See `impl serde::Serialize for DittoAttachment`
63#[derive(Deserialize)]
64struct DittoAttachmentTokenStoredFormat {
65    /// FIXME(Daniel): we have to use `alias` instead of `rename` so as to avoid
66    /// breakage with older Rust SDK versions using `_type` as the key name.
67    #[serde(alias = "_ditto_internal_type_jkb12973t4b")]
68    _type: u64,
69    #[serde(with = "serde_bytes")]
70    _id: Box<[u8]>,
71    _len: u64,
72    _meta: HashMap<String, String>,
73}
74
75/// Trait abstracting over [`DittoAttachmentToken`] and a
76/// [`QueryResultItem::value()`][crate::store::dql::QueryResultItem::value] matching an input
77/// [`DittoAttachment`][super::DittoAttachment].
78pub trait DittoAttachmentTokenLike {
79    fn parse_attachment_token(self) -> Result<DittoAttachmentToken, DittoError>;
80}
81
82impl DittoAttachmentTokenLike for DittoAttachmentToken {
83    fn parse_attachment_token(self) -> Result<DittoAttachmentToken, DittoError> {
84        Ok(self)
85    }
86}
87
88impl DittoAttachmentTokenLike for &'_ BTreeMap<CborValue, CborValue> {
89    fn parse_attachment_token(self) -> Result<DittoAttachmentToken, DittoError> {
90        #[derive(Deserialize)]
91        struct DittoAttachmentTokenStoredFormatInQueryResultItem {
92            id: Box<str>,
93            len: u64,
94            metadata: HashMap<String, String>,
95        }
96
97        let DittoAttachmentTokenStoredFormatInQueryResultItem { id, len, metadata } =
98            ::serde_cbor::from_slice(&::serde_cbor::to_vec(self)?)?;
99        let id = crate::utils::base64_decode_unpadded(&id).map_err(|err| {
100            DittoError::from_str(
101                ErrorKind::InvalidInput,
102                format!("expected base64 `.id` field, got: {err}"),
103            )
104        })?;
105        Ok(DittoAttachmentToken { id, len, metadata })
106    }
107}
108
109impl DittoAttachmentTokenLike for BTreeMap<CborValue, CborValue> {
110    fn parse_attachment_token(self) -> Result<DittoAttachmentToken, DittoError> {
111        (&self).parse_attachment_token()
112    }
113}