1
use std::{any::type_name, collections::HashMap, marker::PhantomData};
2

            
3
use bonsaidb_core::{
4
    arc_bytes::serde::Bytes,
5
    document::DocumentId,
6
    schema::CollectionName,
7
    transaction::{ChangedDocument, ChangedKey, Changes, DocumentChanges},
8
};
9
use serde::{Deserialize, Serialize};
10
use transmog_versions::Versioned;
11

            
12
#[derive(thiserror::Error)]
13
pub struct UnknownVersion<T>(PhantomData<T>);
14

            
15
impl<T> Default for UnknownVersion<T> {
16
    fn default() -> Self {
17
        Self(PhantomData)
18
    }
19
}
20

            
21
impl<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

            
29
impl<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)]
36
enum ChangesVersions {
37
    Legacy = 0,
38
    V1 = 1,
39
}
40

            
41
impl Versioned for ChangesVersions {
42
619029
    fn version(&self) -> u64 {
43
619029
        *self as u64
44
619029
    }
45
}
46

            
47
impl TryFrom<u64> for ChangesVersions {
48
    type Error = UnknownVersion<Changes>;
49

            
50
306884
    fn try_from(value: u64) -> Result<Self, Self::Error> {
51
306884
        match value {
52
64
            0 => Ok(ChangesVersions::Legacy),
53
306820
            1 => Ok(ChangesVersions::V1),
54
            _ => Err(UnknownVersion::default()),
55
        }
56
306884
    }
57
}
58

            
59
306884
pub fn deserialize_executed_transaction_changes(data: &[u8]) -> Result<Changes, crate::Error> {
60
306884
    let (version, data) = transmog_versions::unwrap_version(data);
61
306884
    match ChangesVersions::try_from(version)? {
62
        ChangesVersions::Legacy => {
63
64
            let legacy: ChangesV0 = match pot::from_slice(data) {
64
64
                Ok(changes) => changes,
65
                Err(pot::Error::NotAPot) => ChangesV0::Documents(bincode::deserialize(data)?),
66
                other => other?,
67
            };
68
64
            Changes::try_from(legacy).map_err(crate::Error::from)
69
        }
70
306820
        ChangesVersions::V1 => pot::from_slice(data).map_err(crate::Error::from),
71
    }
72
306884
}
73

            
74
619029
pub fn serialize_executed_transaction_changes(changes: &Changes) -> Result<Vec<u8>, crate::Error> {
75
619029
    let mut serialized = Vec::new();
76
619029
    transmog_versions::write_header(&ChangesVersions::V1, &mut serialized)?;
77
619029
    pot::to_writer(changes, &mut serialized)?;
78
619029
    Ok(serialized)
79
619029
}
80

            
81
/// A list of changes.
82
128
#[derive(Clone, Debug, Serialize, Deserialize)]
83
pub enum ChangesV0 {
84
    /// A list of changed documents.
85
    Documents(Vec<ChangedDocumentV0>),
86
    /// A list of changed keys.
87
    Keys(Vec<ChangedKey>),
88
}
89

            
90
impl TryFrom<ChangesV0> for Changes {
91
    type Error = bonsaidb_core::Error;
92
64
    fn try_from(legacy: ChangesV0) -> Result<Self, Self::Error> {
93
64
        match legacy {
94
48
            ChangesV0::Documents(legacy_documents) => {
95
48
                let mut changed_documents = Vec::with_capacity(legacy_documents.len());
96
48
                let mut collections = Vec::new();
97
48
                let mut collection_indexes = HashMap::new();
98
96
                for changed in legacy_documents {
99
48
                    let collection = if let Some(id) = collection_indexes.get(&changed.collection) {
100
                        *id
101
                    } else {
102
48
                        let id = u16::try_from(collections.len()).unwrap();
103
48
                        collection_indexes.insert(changed.collection.clone(), id);
104
48
                        collections.push(changed.collection);
105
48
                        id
106
                    };
107
                    changed_documents.push(ChangedDocument {
108
48
                        collection,
109
48
                        id: changed.id.try_into()?,
110
48
                        deleted: changed.deleted,
111
                    });
112
                }
113
48
                Ok(Self::Documents(DocumentChanges {
114
48
                    collections,
115
48
                    documents: changed_documents,
116
48
                }))
117
            }
118
16
            ChangesV0::Keys(changes) => Ok(Self::Keys(changes)),
119
        }
120
64
    }
121
}
122

            
123
/// A record of a changed document.
124
336
#[derive(Debug, Clone, Serialize, Deserialize)]
125
pub struct ChangedDocumentV0 {
126
    /// The id of the `Collection` of the changed `Document`.
127
    pub collection: CollectionName,
128

            
129
    /// The id of the changed `Document`.
130
    pub id: LegacyDocumentId,
131

            
132
    /// If the `Document` has been deleted, this will be `true`.
133
    pub deleted: bool,
134
}
135

            
136
72
#[derive(Debug, Clone, Serialize, Deserialize)]
137
#[serde(untagged)]
138
pub enum LegacyDocumentId {
139
    U64(u64),
140
    Document(Bytes),
141
}
142

            
143
impl TryFrom<LegacyDocumentId> for DocumentId {
144
    type Error = bonsaidb_core::Error;
145
48
    fn try_from(id: LegacyDocumentId) -> Result<Self, Self::Error> {
146
48
        match id {
147
24
            LegacyDocumentId::Document(id) => DocumentId::try_from(&id[..]),
148
24
            LegacyDocumentId::U64(version) => Ok(DocumentId::from_u64(version)),
149
        }
150
48
    }
151
}