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)]
126 pub(super) struct SerializedDiff {
127 pub(super) insertions: Vec<usize>,
128 pub(super) deletions: Vec<usize>,
129 pub(super) updates: Vec<usize>,
130 pub(super) moves: Vec<SerializedDiffMove>,
131 }
132
133 #[derive(Debug, PartialEq, Eq)]
138 pub(super) struct SerializedDiffMove {
139 pub(super) from: usize,
140 pub(super) to: usize,
141 }
142
143 impl From<(usize, usize)> for SerializedDiffMove {
144 fn from((from, to): (usize, usize)) -> Self {
145 Self { from, to }
146 }
147 }
148
149 impl<'de> serde::Deserialize<'de> for SerializedDiffMove {
152 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153 where
154 D: serde::de::Deserializer<'de>,
155 {
156 let arr: Vec<usize> = serde::Deserialize::deserialize(deserializer)?;
157 if arr.len() != 2 {
158 return Err(serde::de::Error::custom(
159 "DiffMove must be a 2-element array",
160 ));
161 }
162 Ok(SerializedDiffMove {
163 from: arr[0],
164 to: arr[1],
165 })
166 }
167 }
168
169 impl serde::Serialize for SerializedDiffMove {
170 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171 where
172 S: serde::Serializer,
173 {
174 use serde::ser::SerializeSeq;
175 let mut state = serializer.serialize_seq(Some(2))?;
176 state.serialize_element(&self.from)?;
177 state.serialize_element(&self.to)?;
178 state.end()
179 }
180 }
181
182 #[cfg(test)]
183 mod tests {
184 use super::*;
185
186 #[test]
187 fn diff_serialization_and_deserialization_works() {
188 let diff = SerializedDiff {
189 insertions: [0, 1, 2].into(),
190 deletions: [3, 4].into(),
191 updates: [5].into(),
192 moves: vec![(6, 7), (8, 9)].into_iter().map(Into::into).collect(),
193 };
194
195 let cbor_data = serde_cbor::to_vec(&diff).unwrap();
196
197 let deserialized_diff: SerializedDiff = serde_cbor::from_slice(&cbor_data).unwrap();
198
199 assert_eq!(diff.insertions, deserialized_diff.insertions);
200 assert_eq!(diff.deletions, deserialized_diff.deletions);
201 assert_eq!(diff.updates, deserialized_diff.updates);
202 assert_eq!(diff.moves, deserialized_diff.moves);
203 }
204 }
205}