1use khive_types::{Entity, Header, Link, Note};
4
5use crate::{
6 DeleteOp, DeletePreimage, EdgePreimage, EntityPreimage, NotePreimage, UpdateOp, UpdatePreimage,
7};
8
9#[derive(Clone, Copy, Debug)]
10pub enum CanonicalRecordRef<'a> {
12 Entity(&'a Entity),
13 Note(&'a Note),
14 Edge(&'a Link),
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct PreimageMismatch {
20 pub field_path: String,
21}
22
23impl std::fmt::Display for PreimageMismatch {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 write!(f, "captured preimage differs at {}", self.field_path)
26 }
27}
28
29impl std::error::Error for PreimageMismatch {}
30
31fn equal<T: PartialEq + ?Sized>(
32 field: &str,
33 captured: &T,
34 live: &T,
35) -> Result<(), PreimageMismatch> {
36 if captured == live {
37 Ok(())
38 } else {
39 Err(PreimageMismatch {
40 field_path: field.into(),
41 })
42 }
43}
44
45fn touched<T: PartialEq>(
46 field: &str,
47 captured: &Option<T>,
48 live: &T,
49) -> Result<(), PreimageMismatch> {
50 match captured {
51 Some(captured) => equal(field, captured, live),
52 None => Ok(()),
53 }
54}
55
56pub fn check_update(op: &UpdateOp, live: CanonicalRecordRef<'_>) -> Result<(), PreimageMismatch> {
58 match (op.preimage(), live) {
59 (UpdatePreimage::Entity(preimage), CanonicalRecordRef::Entity(live)) => {
60 equal("id", &op.target_id(), &live.header.id)?;
61 let EntityPreimage {
62 name,
63 description,
64 properties,
65 tags,
66 entity_type,
67 } = preimage;
68 touched("name", name, &live.name)?;
69 touched("description", description, &live.description)?;
70 touched("properties", properties, &live.properties)?;
71 touched("tags", tags, &live.tags)?;
72 touched("entity_type", entity_type, &live.entity_type)
73 }
74 (UpdatePreimage::Note(preimage), CanonicalRecordRef::Note(live)) => {
75 equal("id", &op.target_id(), &live.header.id)?;
76 let NotePreimage {
77 content,
78 salience,
79 decay_factor,
80 properties,
81 tags,
82 } = preimage;
83 touched("content", content, &live.content)?;
84 touched("salience", salience, &live.salience)?;
85 touched("decay_factor", decay_factor, &live.decay_factor)?;
86 touched("properties", properties, &live.properties)?;
87 touched("tags", tags, &live.tags)
88 }
89 (UpdatePreimage::Edge(preimage), CanonicalRecordRef::Edge(live)) => {
90 equal("id", &op.target_id(), &live.id)?;
91 let EdgePreimage { relation, weight } = preimage;
92 touched("relation", relation, &live.relation)?;
93 touched("weight", weight, &live.weight)
94 }
95 _ => Err(PreimageMismatch {
96 field_path: "substrate".into(),
97 }),
98 }
99}
100
101fn header(captured: &Header, live: &Header) -> Result<(), PreimageMismatch> {
102 let Header {
103 id,
104 namespace,
105 created_at,
106 updated_at: _,
107 } = captured;
108 equal("id", id, &live.id)?;
109 equal("namespace", namespace, &live.namespace)?;
110 equal("created_at", created_at, &live.created_at)
111}
112
113pub fn check_delete(op: &DeleteOp, live: CanonicalRecordRef<'_>) -> Result<(), PreimageMismatch> {
115 match (&op.preimage, live) {
116 (DeletePreimage::Entity(captured), CanonicalRecordRef::Entity(live)) => {
117 equal("id", &op.target_id, &live.header.id)?;
118 let Entity {
119 header: captured_header,
120 kind,
121 entity_type,
122 name,
123 description,
124 properties,
125 tags,
126 deleted_at,
127 } = captured.as_ref();
128 header(captured_header, &live.header)?;
129 equal("kind", kind, &live.kind)?;
130 equal("entity_type", entity_type, &live.entity_type)?;
131 equal("name", name, &live.name)?;
132 equal("description", description, &live.description)?;
133 equal("properties", properties, &live.properties)?;
134 equal("tags", tags, &live.tags)?;
135 equal("deleted_at", deleted_at, &live.deleted_at)
136 }
137 (DeletePreimage::Note(captured), CanonicalRecordRef::Note(live)) => {
138 equal("id", &op.target_id, &live.header.id)?;
139 let Note {
140 header: captured_header,
141 kind,
142 status,
143 content,
144 properties,
145 tags,
146 salience,
147 decay_factor,
148 expires_at,
149 deleted_at,
150 } = captured.as_ref();
151 header(captured_header, &live.header)?;
152 equal("kind", kind, &live.kind)?;
153 equal("status", status, &live.status)?;
154 equal("content", content, &live.content)?;
155 equal("properties", properties, &live.properties)?;
156 equal("tags", tags, &live.tags)?;
157 equal("salience", salience, &live.salience)?;
158 equal("decay_factor", decay_factor, &live.decay_factor)?;
159 equal("expires_at", expires_at, &live.expires_at)?;
160 equal("deleted_at", deleted_at, &live.deleted_at)
161 }
162 (DeletePreimage::Edge(captured), CanonicalRecordRef::Edge(live)) => {
163 equal("id", &op.target_id, &live.id)?;
164 let Link {
165 id,
166 namespace,
167 source,
168 target,
169 relation,
170 properties,
171 weight,
172 created_at,
173 updated_at: _,
174 deleted_at,
175 } = captured.as_ref();
176 equal("id", id, &live.id)?;
177 equal("namespace", namespace, &live.namespace)?;
178 equal("source", source, &live.source)?;
179 equal("target", target, &live.target)?;
180 equal("relation", relation, &live.relation)?;
181 equal("properties", properties, &live.properties)?;
182 equal("weight", weight, &live.weight)?;
183 equal("created_at", created_at, &live.created_at)?;
184 equal("deleted_at", deleted_at, &live.deleted_at)
185 }
186 _ => Err(PreimageMismatch {
187 field_path: "substrate".into(),
188 }),
189 }
190}
191
192#[cfg(test)]
193#[path = "preimage_tests.rs"]
194mod tests;