oxide_mirror/event.rs
1//! Delta + record shapes shared across the mirror.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6/// Whether a delta inserts/updates or deletes a record.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum DeltaOp {
10 /// Insert the record if it does not exist, or replace the payload.
11 Upsert,
12 /// Delete the record. `payload` is ignored.
13 Delete,
14}
15
16/// A single change-set emitted by a [`SyncSource`](crate::source::SyncSource).
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Delta {
19 /// Resource (table) the delta applies to, e.g. `"pets"`.
20 pub resource: String,
21 /// Stable id of the record within the resource.
22 pub record_id: String,
23 /// Operation kind.
24 pub op: DeltaOp,
25 /// Record payload (JSON object). For `Delete` this is typically
26 /// [`serde_json::Value::Null`].
27 pub payload: serde_json::Value,
28 /// When the source observed the change.
29 pub occurred_at: DateTime<Utc>,
30 /// Source + confidence metadata.
31 pub provenance: Provenance,
32}
33
34impl Delta {
35 /// Convenience: build an upsert delta.
36 pub fn upsert(
37 resource: impl Into<String>,
38 record_id: impl Into<String>,
39 payload: serde_json::Value,
40 source: impl Into<String>,
41 ) -> Self {
42 Self {
43 resource: resource.into(),
44 record_id: record_id.into(),
45 op: DeltaOp::Upsert,
46 payload,
47 occurred_at: Utc::now(),
48 provenance: Provenance::new(source),
49 }
50 }
51
52 /// Convenience: build a delete delta.
53 pub fn delete(
54 resource: impl Into<String>,
55 record_id: impl Into<String>,
56 source: impl Into<String>,
57 ) -> Self {
58 Self {
59 resource: resource.into(),
60 record_id: record_id.into(),
61 op: DeltaOp::Delete,
62 payload: serde_json::Value::Null,
63 occurred_at: Utc::now(),
64 provenance: Provenance::new(source),
65 }
66 }
67
68 /// Builder helper to override the default confidence.
69 #[must_use]
70 pub fn with_confidence(mut self, confidence: f32) -> Self {
71 self.provenance.confidence = confidence.clamp(0.0, 1.0);
72 self
73 }
74
75 /// Builder helper to set a specific occurrence time.
76 #[must_use]
77 pub fn at(mut self, ts: DateTime<Utc>) -> Self {
78 self.occurred_at = ts;
79 self
80 }
81}
82
83/// Source + confidence metadata attached to every delta and stored alongside
84/// every mirrored record.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct Provenance {
87 /// Stable id of the source that produced the delta.
88 pub source: String,
89 /// Confidence in the payload, in `[0.0, 1.0]`. Sources that have no
90 /// notion of confidence should report `1.0`.
91 pub confidence: f32,
92}
93
94impl Provenance {
95 /// Build a provenance with `confidence = 1.0`.
96 pub fn new(source: impl Into<String>) -> Self {
97 Self {
98 source: source.into(),
99 confidence: 1.0,
100 }
101 }
102}
103
104/// A materialised record in the local mirror.
105///
106/// Every column on this struct maps to a column in `mirror_records`.
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct MirroredRecord {
109 /// Resource the record belongs to.
110 pub resource: String,
111 /// Stable record id.
112 pub record_id: String,
113 /// Last successfully-applied payload.
114 pub payload: serde_json::Value,
115 /// Source that wrote the latest version.
116 pub source: String,
117 /// Wall-clock time of the latest successful apply.
118 pub last_synced_at: DateTime<Utc>,
119 /// Confidence of the latest payload.
120 pub confidence: f32,
121 /// Monotonic per-record version, incremented on every successful apply.
122 pub version: i64,
123}