dittolive_ditto/dql/
differ.rs1use std::{collections::HashSet, fmt};
2
3use ffi_sdk::{self, ffi_utils::repr_c};
4
5use crate::dql::QueryResultItem;
6
7#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
9pub struct DiffMove {
10 pub from: usize,
12
13 pub to: usize,
15}
16
17impl From<(usize, usize)> for DiffMove {
18 fn from((from, to): (usize, usize)) -> Self {
19 Self { from, to }
20 }
21}
22
23#[derive(Debug, Clone, Eq, PartialEq, Default)]
34#[non_exhaustive]
35pub struct Diff {
36 pub insertions: HashSet<usize>,
38
39 pub deletions: HashSet<usize>,
41
42 pub updates: HashSet<usize>,
44
45 pub moves: HashSet<DiffMove>,
48}
49
50impl Diff {
51 pub fn empty() -> Self {
55 Self::default()
56 }
57}
58
59pub struct Differ {
65 raw: repr_c::Box<ffi_sdk::FfiDiffer>,
66}
67
68impl Differ {
69 #[inline]
71 pub fn new() -> Self {
72 Self {
73 raw: ffi_sdk::dittoffi_differ_new(),
74 }
75 }
76
77 pub fn diff(&self, items: impl IntoIterator<Item = QueryResultItem>) -> Diff {
90 let raw_items: Vec<_> = items.into_iter().map(|item| item.raw.clone()).collect();
91 let diff_cbor = ffi_sdk::dittoffi_differ_diff(&self.raw, raw_items.as_slice().into());
92 let deserialized: serialization::SerializedDiff =
93 ::serde_cbor::from_slice(&diff_cbor.0).unwrap();
94 Diff {
95 insertions: deserialized.insertions.into_iter().collect(),
96 deletions: deserialized.deletions.into_iter().collect(),
97 updates: deserialized.updates.into_iter().collect(),
98 moves: deserialized
99 .moves
100 .into_iter()
101 .map(|m| DiffMove::from((m.from, m.to)))
102 .collect(),
103 }
104 }
105}
106
107impl fmt::Debug for Differ {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 f.debug_struct("Differ").finish()
110 }
111}
112
113impl Default for Differ {
114 #[inline]
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120mod serialization {
122 #[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
124 pub(super) struct SerializedDiff {
125 pub(super) insertions: Vec<usize>,
126 pub(super) deletions: Vec<usize>,
127 pub(super) updates: Vec<usize>,
128 pub(super) moves: Vec<SerializedDiffMove>,
129 }
130
131 #[derive(Debug, PartialEq, Eq)]
133 pub(super) struct SerializedDiffMove {
134 pub(super) from: usize,
135 pub(super) to: usize,
136 }
137
138 impl From<(usize, usize)> for SerializedDiffMove {
139 fn from((from, to): (usize, usize)) -> Self {
140 Self { from, to }
141 }
142 }
143
144 impl<'de> serde::Deserialize<'de> for SerializedDiffMove {
147 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148 where
149 D: serde::de::Deserializer<'de>,
150 {
151 let arr: Vec<usize> = serde::Deserialize::deserialize(deserializer)?;
152 if arr.len() != 2 {
153 return Err(serde::de::Error::custom(
154 "DiffMove must be a 2-element array",
155 ));
156 }
157 Ok(SerializedDiffMove {
158 from: arr[0],
159 to: arr[1],
160 })
161 }
162 }
163
164 impl serde::Serialize for SerializedDiffMove {
165 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166 where
167 S: serde::Serializer,
168 {
169 use serde::ser::SerializeSeq;
170 let mut state = serializer.serialize_seq(Some(2))?;
171 state.serialize_element(&self.from)?;
172 state.serialize_element(&self.to)?;
173 state.end()
174 }
175 }
176
177 #[cfg(test)]
178 mod tests {
179 use super::*;
180
181 #[test]
182 fn diff_serialization_and_deserialization_works() {
183 let diff = SerializedDiff {
184 insertions: [0, 1, 2].into(),
185 deletions: [3, 4].into(),
186 updates: [5].into(),
187 moves: vec![(6, 7), (8, 9)].into_iter().map(Into::into).collect(),
188 };
189
190 let cbor_data = serde_cbor::to_vec(&diff).unwrap();
191
192 let deserialized_diff: SerializedDiff = serde_cbor::from_slice(&cbor_data).unwrap();
193
194 assert_eq!(diff.insertions, deserialized_diff.insertions);
195 assert_eq!(diff.deletions, deserialized_diff.deletions);
196 assert_eq!(diff.updates, deserialized_diff.updates);
197 assert_eq!(diff.moves, deserialized_diff.moves);
198 }
199 }
200}