use std::{collections::HashSet, fmt};
use ffi_sdk::{self, ffi_utils::repr_c};
use crate::dql::QueryResultItem;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DiffMove {
pub from: usize,
pub to: usize,
}
impl From<(usize, usize)> for DiffMove {
fn from((from, to): (usize, usize)) -> Self {
Self { from, to }
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
#[non_exhaustive]
pub struct Diff {
pub insertions: HashSet<usize>,
pub deletions: HashSet<usize>,
pub updates: HashSet<usize>,
pub moves: HashSet<DiffMove>,
}
impl Diff {
pub fn empty() -> Self {
Self::default()
}
}
pub struct Differ {
raw: repr_c::Box<ffi_sdk::FfiDiffer>,
}
impl Differ {
#[inline]
pub fn new() -> Self {
Self {
raw: ffi_sdk::dittoffi_differ_new(),
}
}
pub fn diff(&self, items: impl IntoIterator<Item = QueryResultItem>) -> Diff {
let raw_items: Vec<_> = items.into_iter().map(|item| item.raw.clone()).collect();
let diff_cbor = ffi_sdk::dittoffi_differ_diff(&self.raw, raw_items.as_slice().into());
let deserialized: serialization::SerializedDiff =
::serde_cbor::from_slice(&diff_cbor.0).unwrap();
Diff {
insertions: deserialized.insertions.into_iter().collect(),
deletions: deserialized.deletions.into_iter().collect(),
updates: deserialized.updates.into_iter().collect(),
moves: deserialized
.moves
.into_iter()
.map(|m| DiffMove::from((m.from, m.to)))
.collect(),
}
}
}
impl fmt::Debug for Differ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Differ").finish()
}
}
impl Default for Differ {
#[inline]
fn default() -> Self {
Self::new()
}
}
mod serialization {
#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub(super) struct SerializedDiff {
pub(super) insertions: Vec<usize>,
pub(super) deletions: Vec<usize>,
pub(super) updates: Vec<usize>,
pub(super) moves: Vec<SerializedDiffMove>,
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct SerializedDiffMove {
pub(super) from: usize,
pub(super) to: usize,
}
impl From<(usize, usize)> for SerializedDiffMove {
fn from((from, to): (usize, usize)) -> Self {
Self { from, to }
}
}
impl<'de> serde::Deserialize<'de> for SerializedDiffMove {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let arr: Vec<usize> = serde::Deserialize::deserialize(deserializer)?;
if arr.len() != 2 {
return Err(serde::de::Error::custom(
"DiffMove must be a 2-element array",
));
}
Ok(SerializedDiffMove {
from: arr[0],
to: arr[1],
})
}
}
impl serde::Serialize for SerializedDiffMove {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut state = serializer.serialize_seq(Some(2))?;
state.serialize_element(&self.from)?;
state.serialize_element(&self.to)?;
state.end()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diff_serialization_and_deserialization_works() {
let diff = SerializedDiff {
insertions: [0, 1, 2].into(),
deletions: [3, 4].into(),
updates: [5].into(),
moves: vec![(6, 7), (8, 9)].into_iter().map(Into::into).collect(),
};
let cbor_data = serde_cbor::to_vec(&diff).unwrap();
let deserialized_diff: SerializedDiff = serde_cbor::from_slice(&cbor_data).unwrap();
assert_eq!(diff.insertions, deserialized_diff.insertions);
assert_eq!(diff.deletions, deserialized_diff.deletions);
assert_eq!(diff.updates, deserialized_diff.updates);
assert_eq!(diff.moves, deserialized_diff.moves);
}
}
}