1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use_prelude!();

use crate::error::{DittoError, ErrorKind};

#[derive(Debug, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct DocumentId {
    pub(crate) bytes: Vec<u8>,
}

impl DocumentId {
    pub fn new<V: ::serde::Serialize>(value: &V) -> Result<Self, DittoError> {
        use ::safer_ffi::prelude::{AsOut, ManuallyDropMut};

        let cbor_bytes = ::serde_cbor::to_vec(value).unwrap();

        let mut out_cbor_slot = None;
        let out_cbor = out_cbor_slot.manually_drop_mut().as_out();
        unsafe {
            let res = ffi_sdk::ditto_validate_document_id((&cbor_bytes[..]).into(), out_cbor);
            if res != 0 {
                return Err(DittoError::from_ffi(ErrorKind::Internal));
            }
        }

        let bytes = match out_cbor_slot {
            None => cbor_bytes,
            Some(cbor_boxed_slice) => cbor_boxed_slice.as_slice().to_vec(),
        };

        Ok(Self { bytes })
    }

    pub fn to_query_compatible(
        &self,
        string_primitive_fmt: ffi_sdk::StringPrimitiveFormat,
    ) -> String {
        unsafe {
            let str_boxed = ffi_sdk::ditto_document_id_query_compatible(
                self.bytes.as_slice().into(),
                string_primitive_fmt,
            );
            str_boxed.to_str().to_owned()
        }
    }

    pub fn value(&self) -> ::serde_cbor::Value {
        self.to_cbor()
    }

    pub fn to_cbor(&self) -> ::serde_cbor::Value {
        ::serde_cbor::from_slice(&self.bytes[..]).expect("DocumentId can be represented as CBOR")
    }
}

impl From<Vec<u8>> for DocumentId {
    fn from(bytes: Vec<u8>) -> Self {
        Self { bytes }
    }
}

impl From<Box<[u8]>> for DocumentId {
    fn from(bytes: Box<[u8]>) -> Self {
        Self {
            bytes: bytes.into(),
        }
    }
}

impl From<&[u8]> for DocumentId {
    fn from(slice: &[u8]) -> Self {
        Self {
            bytes: slice.to_owned().to_vec(),
        }
    }
}

impl AsRef<[u8]> for DocumentId {
    fn as_ref(&self) -> &[u8] {
        &self.bytes[..]
    }
}

impl std::fmt::Display for DocumentId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            self.to_query_compatible(ffi_sdk::StringPrimitiveFormat::WithoutQuotes)
        )
    }
}

// TODO: (Ham) Is this reasonable? Can we transcode somehow instead?

impl serde::Serialize for DocumentId {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let s: serde_cbor::Value = serde_cbor::from_slice(&self.bytes[..]).unwrap();
        s.serialize(serializer)
    }
}

impl<'de> serde::de::Deserialize<'de> for DocumentId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        Ok(Self {
            bytes: serde_cbor::to_vec(&serde_cbor::Value::deserialize(deserializer)?).unwrap(),
        })
    }
}