use chrono::{serde::ts_milliseconds, DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::collections::{HashMap, VecDeque}; #[derive(thiserror::Error, Debug, Serialize)] pub enum Error { #[error("Missing change {0:?}")] MissingChange(Vec), #[error("Unexpected changes {0:?}")] UnexpectedChanges(Vec>), #[error("Bincode: {0}")] #[serde(serialize_with = "crate::error::serialize_to_string")] Bincode(#[from] bincode::Error), } #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(bound = "T: Serialize + DeserializeOwned")] pub struct Changelog { #[serde(skip)] pub previous: Option>, #[serde(skip)] pub object_id: Vec, #[serde(flatten)] pub change: T, #[serde(with = "ts_milliseconds")] pub updated_at: DateTime, } impl Changelog { pub fn new(previous: Option>, object_id: Vec, change: T) -> Changelog { Self { previous, object_id, change, updated_at: Utc::now(), } } pub fn new_from_db( previous: Option>, object_id: Vec, change: T, created_at: DateTime, ) -> Changelog { Self { previous, object_id, change, updated_at: created_at, } } pub fn id(&self) -> Result, Error> { let mut hasher = Sha256::new(); hasher.update(&self.object_id); hasher.update(if let Some(v) = self.previous.as_ref() { v.clone() } else { vec![0, 0] }); hasher.update(&bincode::serialize(&self.change)?); hasher.update(&bincode::serialize(&self.updated_at)?); Ok(hasher.finalize().to_vec()) } } pub fn sort_changes( changes: Vec>, last_change: Vec, ) -> Result>, Error> { let mut changes_by_id = changes .into_iter() .map(|a| a.id().map(|id| (id, a))) .collect::, Changelog>, _>>()?; let mut sorted_changes = VecDeque::new(); let last_change = match changes_by_id.remove(&last_change) { Some(change) => change, None => return Err(Error::MissingChange(last_change)), }; sorted_changes.push_front(last_change); loop { let first_element = sorted_changes.get(0).unwrap(); if let Some(id) = first_element.previous.as_ref() { let last_change = match changes_by_id.remove(id) { Some(change) => change, None => return Err(Error::MissingChange(id.clone())), }; sorted_changes.push_front(last_change); } else { break; } } if !changes_by_id.is_empty() { return Err(Error::UnexpectedChanges( changes_by_id.into_keys().collect(), )); } Ok(sorted_changes.into()) }