sos_core/events/
patch.rs

1//! Patch and diff types for events.
2use crate::{
3    commit::{CommitHash, CommitProof},
4    events::{AccountEvent, DeviceEvent, EventRecord, WriteEvent},
5    Result,
6};
7use binary_stream::futures::{Decodable, Encodable};
8use std::marker::PhantomData;
9
10#[cfg(feature = "files")]
11use crate::events::FileEvent;
12
13/// Patch of account events.
14pub type AccountPatch = Patch<AccountEvent>;
15
16/// Patch of folder events.
17pub type FolderPatch = Patch<WriteEvent>;
18
19/// Patch of device events.
20pub type DevicePatch = Patch<DeviceEvent>;
21
22/// Patch of file events.
23#[cfg(feature = "files")]
24pub type FilePatch = Patch<FileEvent>;
25
26/// Patch wraps a changeset of events to be sent across the network.
27#[derive(Clone, Debug, Default, Eq, PartialEq)]
28pub struct Patch<T>(Vec<EventRecord>, PhantomData<T>);
29
30impl<T> Patch<T> {
31    /// Create a new patch from event records.
32    pub fn new(records: Vec<EventRecord>) -> Self {
33        Self(records, PhantomData)
34    }
35
36    /// Number of events in this patch.
37    pub fn len(&self) -> usize {
38        self.0.len()
39    }
40
41    /// Whether this patch is empty.
42    pub fn is_empty(&self) -> bool {
43        self.0.is_empty()
44    }
45
46    /// Iterator of the event records.
47    pub fn iter(&self) -> impl Iterator<Item = &EventRecord> {
48        self.0.iter()
49    }
50
51    /// Mutable event records.
52    pub fn records(&self) -> &[EventRecord] {
53        self.0.as_slice()
54    }
55
56    /// Decode this patch into the events.
57    pub async fn into_events<E: Default + Decodable + Encodable>(
58        &self,
59    ) -> Result<Vec<E>> {
60        let mut events = Vec::with_capacity(self.0.len());
61        for record in &self.0 {
62            events.push(record.decode_event::<E>().await?);
63        }
64        Ok(events)
65    }
66}
67
68impl<T> From<Patch<T>> for Vec<EventRecord> {
69    fn from(value: Patch<T>) -> Self {
70        value.0
71    }
72}
73
74/// Result of a checked patch on an event log.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum CheckedPatch {
77    /// Patch was applied.
78    Success(CommitProof),
79    /// Patch conflict.
80    Conflict {
81        /// Head of the event log.
82        head: CommitProof,
83        /// If the checked proof is contained
84        /// in the event log.
85        contains: Option<CommitProof>,
86    },
87}
88
89/// Diff between local and remote.
90#[derive(Default, Debug, Clone, PartialEq, Eq)]
91pub struct Diff<T> {
92    /// Contents of the patch.
93    pub patch: Patch<T>,
94    /// Checkpoint for the diff patch.
95    ///
96    /// For checked patches this must match the proof
97    /// of HEAD before the patch was created.
98    ///
99    /// For unchecked force merges this checkpoint
100    /// references the commit proof of HEAD after
101    /// applying the patch.
102    pub checkpoint: CommitProof,
103    /// Last commit hash before the patch was created.
104    ///
105    /// This can be used to determine if the patch is to
106    /// be used to initialize a new set of events when
107    /// no last commit is available.
108    ///
109    /// For example, for file event logs which are
110    /// lazily instantiated once external files are created.
111    pub last_commit: Option<CommitHash>,
112}
113
114impl<T> Diff<T> {
115    /// Create a diff.
116    pub fn new(
117        patch: Patch<T>,
118        checkpoint: CommitProof,
119        last_commit: Option<CommitHash>,
120    ) -> Self {
121        Self {
122            patch,
123            checkpoint,
124            last_commit,
125        }
126    }
127}
128
129/// Diff between account events logs.
130pub type AccountDiff = Diff<AccountEvent>;
131
132/// Diff between device events logs.
133pub type DeviceDiff = Diff<DeviceEvent>;
134
135/// Diff between file events logs.
136#[cfg(feature = "files")]
137pub type FileDiff = Diff<FileEvent>;
138
139/// Diff between folder events logs.
140pub type FolderDiff = Diff<WriteEvent>;