1
use std::any::type_name;
2
use std::collections::HashMap;
3
use std::marker::PhantomData;
4

            
5
use bonsaidb_core::arc_bytes::serde::Bytes;
6
use bonsaidb_core::document::DocumentId;
7
use bonsaidb_core::schema::CollectionName;
8
use bonsaidb_core::transaction::{ChangedDocument, ChangedKey, Changes, DocumentChanges};
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
754018
    fn version(&self) -> u64 {
43
754018
        *self as u64
44
754018
    }
45
}
46

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

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

            
59
366446
pub fn deserialize_executed_transaction_changes(data: &[u8]) -> Result<Changes, crate::Error> {
60
366446
    let (version, data) = transmog_versions::unwrap_version(data);
61
366446
    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
366382
        ChangesVersions::V1 => pot::from_slice(data).map_err(crate::Error::from),
71
    }
72
366446
}
73

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

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

            
124
/// A record of a changed document.
125
336
#[derive(Debug, Clone, Serialize, Deserialize)]
126
pub 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
72
#[derive(Debug, Clone, Serialize, Deserialize)]
138
#[serde(untagged)]
139
pub enum LegacyDocumentId {
140
    U64(u64),
141
    Document(Bytes),
142
}
143

            
144
impl TryFrom<LegacyDocumentId> for DocumentId {
145
    type Error = bonsaidb_core::Error;
146

            
147
48
    fn try_from(id: LegacyDocumentId) -> Result<Self, Self::Error> {
148
48
        match id {
149
24
            LegacyDocumentId::Document(id) => DocumentId::try_from(&id[..]),
150
24
            LegacyDocumentId::U64(version) => Ok(DocumentId::from_u64(version)),
151
        }
152
48
    }
153
}