bonsaidb_local/database/
compat.rs

1use std::any::type_name;
2use std::collections::HashMap;
3use std::marker::PhantomData;
4
5use bonsaidb_core::arc_bytes::serde::Bytes;
6use bonsaidb_core::document::DocumentId;
7use bonsaidb_core::schema::CollectionName;
8use bonsaidb_core::transaction::{ChangedDocument, ChangedKey, Changes, DocumentChanges};
9use serde::{Deserialize, Serialize};
10use transmog_versions::Versioned;
11
12#[derive(thiserror::Error)]
13pub struct UnknownVersion<T>(PhantomData<T>);
14
15impl<T> Default for UnknownVersion<T> {
16    fn default() -> Self {
17        Self(PhantomData)
18    }
19}
20
21impl<T> std::fmt::Debug for UnknownVersion<T> {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.debug_tuple("UnknownVersion")
24            .field(&type_name::<T>())
25            .finish()
26    }
27}
28
29impl<T> std::fmt::Display for UnknownVersion<T> {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(f, "incompatilbe version of {}", type_name::<T>())
32    }
33}
34
35#[derive(Clone, Copy, Debug)]
36enum ChangesVersions {
37    Legacy = 0,
38    V1 = 1,
39}
40
41impl Versioned for ChangesVersions {
42    fn version(&self) -> u64 {
43        *self as u64
44    }
45}
46
47impl TryFrom<u64> for ChangesVersions {
48    type Error = UnknownVersion<Changes>;
49
50    fn try_from(value: u64) -> Result<Self, Self::Error> {
51        match value {
52            0 => Ok(ChangesVersions::Legacy),
53            1 => Ok(ChangesVersions::V1),
54            _ => Err(UnknownVersion::default()),
55        }
56    }
57}
58
59pub fn deserialize_executed_transaction_changes(data: &[u8]) -> Result<Changes, crate::Error> {
60    let (version, data) = transmog_versions::unwrap_version(data);
61    match ChangesVersions::try_from(version)? {
62        ChangesVersions::Legacy => {
63            let legacy: ChangesV0 = match pot::from_slice(data) {
64                Ok(changes) => changes,
65                Err(pot::Error::NotAPot) => ChangesV0::Documents(bincode::deserialize(data)?),
66                other => other?,
67            };
68            Changes::try_from(legacy).map_err(crate::Error::from)
69        }
70        ChangesVersions::V1 => pot::from_slice(data).map_err(crate::Error::from),
71    }
72}
73
74pub fn serialize_executed_transaction_changes(changes: &Changes) -> Result<Vec<u8>, crate::Error> {
75    let mut serialized = Vec::new();
76    transmog_versions::write_header(&ChangesVersions::V1, &mut serialized)?;
77    pot::to_writer(changes, &mut serialized)?;
78    Ok(serialized)
79}
80
81/// A list of changes.
82#[derive(Clone, Debug, Serialize, Deserialize)]
83pub enum ChangesV0 {
84    /// A list of changed documents.
85    Documents(Vec<ChangedDocumentV0>),
86    /// A list of changed keys.
87    Keys(Vec<ChangedKey>),
88}
89
90impl TryFrom<ChangesV0> for Changes {
91    type Error = bonsaidb_core::Error;
92
93    fn try_from(legacy: ChangesV0) -> Result<Self, Self::Error> {
94        match legacy {
95            ChangesV0::Documents(legacy_documents) => {
96                let mut changed_documents = Vec::with_capacity(legacy_documents.len());
97                let mut collections = Vec::new();
98                let mut collection_indexes = HashMap::new();
99                for changed in legacy_documents {
100                    let collection = if let Some(id) = collection_indexes.get(&changed.collection) {
101                        *id
102                    } else {
103                        let id = u16::try_from(collections.len()).unwrap();
104                        collection_indexes.insert(changed.collection.clone(), id);
105                        collections.push(changed.collection);
106                        id
107                    };
108                    changed_documents.push(ChangedDocument {
109                        collection,
110                        id: changed.id.try_into()?,
111                        deleted: changed.deleted,
112                    });
113                }
114                Ok(Self::Documents(DocumentChanges {
115                    collections,
116                    documents: changed_documents,
117                }))
118            }
119            ChangesV0::Keys(changes) => Ok(Self::Keys(changes)),
120        }
121    }
122}
123
124/// A record of a changed document.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ChangedDocumentV0 {
127    /// The id of the `Collection` of the changed `Document`.
128    pub collection: CollectionName,
129
130    /// The id of the changed `Document`.
131    pub id: LegacyDocumentId,
132
133    /// If the `Document` has been deleted, this will be `true`.
134    pub deleted: bool,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[serde(untagged)]
139pub enum LegacyDocumentId {
140    U64(u64),
141    Document(Bytes),
142}
143
144impl TryFrom<LegacyDocumentId> for DocumentId {
145    type Error = bonsaidb_core::Error;
146
147    fn try_from(id: LegacyDocumentId) -> Result<Self, Self::Error> {
148        match id {
149            LegacyDocumentId::Document(id) => DocumentId::try_from(&id[..]),
150            LegacyDocumentId::U64(version) => Ok(DocumentId::from_u64(version)),
151        }
152    }
153}