Skip to main content

hayson_engine/
errors.rs

1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct HaysonSchemaError(pub String);
5
6impl fmt::Display for HaysonSchemaError {
7    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
8        write!(f, "{}", self.0)
9    }
10}
11
12impl std::error::Error for HaysonSchemaError {}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct ValidationIssue {
16    pub field: String,
17    pub reason: String,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct HaysonValidationError {
22    pub table: String,
23    pub issues: Vec<ValidationIssue>,
24}
25
26impl fmt::Display for HaysonValidationError {
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28        let detail = self
29            .issues
30            .iter()
31            .map(|issue| format!("{}: {}", issue.field, issue.reason))
32            .collect::<Vec<_>>()
33            .join("; ");
34        write!(f, "record rejected by schema of table \"{}\" — {}", self.table, detail)
35    }
36}
37
38impl std::error::Error for HaysonValidationError {}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct HaysonUniqueViolationError {
42    pub table: String,
43    pub field: String,
44    pub holder: String,
45}
46
47impl fmt::Display for HaysonUniqueViolationError {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(
50            f,
51            "unique field \"{}\" of table \"{}\" is already taken by key \"{}\"",
52            self.field, self.table, self.holder
53        )
54    }
55}
56
57impl std::error::Error for HaysonUniqueViolationError {}
58
59/// A write refused because it broke an `enforce: true` relation — `from.field`'s new value has no
60/// matching record on the `to` side. `value` is the offending value's JSON text.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct HaysonRelationViolationError {
63    pub relation: String,
64    pub table: String,
65    pub field: String,
66    pub value: String,
67}
68
69impl fmt::Display for HaysonRelationViolationError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        write!(
72            f,
73            "relation \"{}\" refuses \"{}\".\"{}\" = {}: no record on the other side of the relation matches it",
74            self.relation, self.table, self.field, self.value
75        )
76    }
77}
78
79impl std::error::Error for HaysonRelationViolationError {}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct HaysonPendingConflictError {
83    pub table: String,
84    pub key: String,
85    pub fields: Vec<String>,
86}
87
88impl fmt::Display for HaysonPendingConflictError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        let list = self
91            .fields
92            .iter()
93            .map(|field| format!("\"{field}\""))
94            .collect::<Vec<_>>()
95            .join(", ");
96        write!(
97            f,
98            "key \"{}\" of table \"{}\" already has a queued change on {} — pass force to overwrite it",
99            self.key, self.table, list
100        )
101    }
102}
103
104impl std::error::Error for HaysonPendingConflictError {}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct HaysonCorruptLogError {
108    pub path: String,
109    pub offset: u64,
110    pub reason: String,
111}
112
113impl fmt::Display for HaysonCorruptLogError {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        write!(
116            f,
117            "log \"{}\" is corrupt at offset {}: {}",
118            self.path, self.offset, self.reason
119        )
120    }
121}
122
123impl std::error::Error for HaysonCorruptLogError {}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct HaysonIndexMismatchError {
127    pub path: String,
128    /// The log the snapshot claimed to describe — on a partitioned table, the one partition whose
129    /// fingerprint disagreed, not the table as a whole.
130    pub log_path: String,
131    /// Byte the snapshot claims to describe `log_path` up to; equal to that log's size, because a
132    /// snapshot whose watermark differs is stale rather than mismatched.
133    pub watermark: u64,
134    pub expected_hash: String,
135    pub actual_hash: String,
136}
137
138impl fmt::Display for HaysonIndexMismatchError {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        write!(
141            f,
142            "snapshot \"{}\" claims watermark and schema match the log, but its content fingerprint ({}) does not match the log's ({}) — left on disk for inspection, not silently discarded",
143            self.path, self.expected_hash, self.actual_hash
144        )
145    }
146}
147
148impl std::error::Error for HaysonIndexMismatchError {}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct HaysonNotFoundError {
152    pub table: String,
153    pub key: String,
154}
155
156impl fmt::Display for HaysonNotFoundError {
157    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158        write!(f, "key \"{}\" is not in table \"{}\"", self.key, self.table)
159    }
160}
161
162impl std::error::Error for HaysonNotFoundError {}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct HaysonQueryParseError {
166    pub position: usize,
167    pub reason: String,
168}
169
170impl fmt::Display for HaysonQueryParseError {
171    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172        write!(
173            f,
174            "query text is invalid at position {}: {}",
175            self.position, self.reason
176        )
177    }
178}
179
180impl std::error::Error for HaysonQueryParseError {}
181
182/// A record cannot be placed in a partition file: its `partitionBy` value cannot name one, or the
183/// write would move an existing key out of the partition it already lives in.
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct HaysonPartitionError {
186    pub table: String,
187    pub field: String,
188    pub reason: String,
189}
190
191impl HaysonPartitionError {
192    pub fn new(table: impl Into<String>, field: impl Into<String>, reason: impl Into<String>) -> Self {
193        HaysonPartitionError {
194            table: table.into(),
195            field: field.into(),
196            reason: reason.into(),
197        }
198    }
199}
200
201impl fmt::Display for HaysonPartitionError {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(
204            f,
205            "partition field \"{}\" of table \"{}\": {}",
206            self.field, self.table, self.reason
207        )
208    }
209}
210
211impl std::error::Error for HaysonPartitionError {}
212
213/// A `cypher` field failed to seal/open, or the keyring passed to `open()` is itself unusable.
214/// `table` empty means the second case — the message drops the "of table" clause then.
215#[derive(Debug, Clone, PartialEq, Eq)]
216pub struct HaysonCipherError {
217    pub table: String,
218    pub field: String,
219    pub reason: String,
220    pub key_id: Option<String>,
221}
222
223impl HaysonCipherError {
224    pub fn new(table: impl Into<String>, field: impl Into<String>, reason: impl Into<String>) -> Self {
225        HaysonCipherError {
226            table: table.into(),
227            field: field.into(),
228            reason: reason.into(),
229            key_id: None,
230        }
231    }
232
233    pub fn with_key_id(mut self, key_id: impl Into<String>) -> Self {
234        self.key_id = Some(key_id.into());
235        self
236    }
237}
238
239impl fmt::Display for HaysonCipherError {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        if self.table.is_empty() {
242            write!(f, "cypher configuration {}", self.reason)
243        } else {
244            write!(
245                f,
246                "encrypted field \"{}\" of table \"{}\" {}",
247                self.field, self.table, self.reason
248            )
249        }
250    }
251}
252
253impl std::error::Error for HaysonCipherError {}