1use thiserror::Error;
4
5use crate::model::{DataType, Id};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ErrorCode {
10 InvalidMagicOrVersion,
12 IndexOutOfBounds,
14 InvalidSignature,
16 InvalidUtf8,
18 MalformedEncoding,
20}
21
22impl ErrorCode {
23 pub fn code(&self) -> &'static str {
25 match self {
26 ErrorCode::InvalidMagicOrVersion => "E001",
27 ErrorCode::IndexOutOfBounds => "E002",
28 ErrorCode::InvalidSignature => "E003",
29 ErrorCode::InvalidUtf8 => "E004",
30 ErrorCode::MalformedEncoding => "E005",
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Error)]
37pub enum DecodeError {
38 #[error("[E001] invalid magic bytes: expected GRC2 or GRC2Z, found {found:?}")]
40 InvalidMagic { found: [u8; 4] },
41
42 #[error("[E001] unsupported version: {version}")]
43 UnsupportedVersion { version: u8 },
44
45 #[error("[E002] {dict} index {index} out of bounds (size: {size})")]
47 IndexOutOfBounds {
48 dict: &'static str,
49 index: usize,
50 size: usize,
51 },
52
53 #[error("[E004] invalid UTF-8 in {field}")]
55 InvalidUtf8 { field: &'static str },
56
57 #[error("[E005] unexpected end of input while reading {context}")]
59 UnexpectedEof { context: &'static str },
60
61 #[error("[E005] varint exceeds maximum length (10 bytes)")]
62 VarintTooLong,
63
64 #[error("[E005] varint overflow (value exceeds u64)")]
65 VarintOverflow,
66
67 #[error("[E005] {field} length {len} exceeds maximum {max}")]
68 LengthExceedsLimit {
69 field: &'static str,
70 len: usize,
71 max: usize,
72 },
73
74 #[error("[E005] invalid op type: {op_type}")]
75 InvalidOpType { op_type: u8 },
76
77 #[error("[E005] invalid data type: {data_type}")]
78 InvalidDataType { data_type: u8 },
79
80 #[error("[E005] invalid embedding sub-type: {sub_type}")]
81 InvalidEmbeddingSubType { sub_type: u8 },
82
83 #[error("[E005] invalid bool value: {value} (expected 0x00 or 0x01)")]
84 InvalidBool { value: u8 },
85
86 #[error("[E005] reserved bits are non-zero in {context}")]
87 ReservedBitsSet { context: &'static str },
88
89 #[error("[E005] POINT latitude {lat} out of range [-90, +90]")]
90 LatitudeOutOfRange { lat: f64 },
91
92 #[error("[E005] POINT longitude {lon} out of range [-180, +180]")]
93 LongitudeOutOfRange { lon: f64 },
94
95 #[error("[E005] position string contains invalid character: {char:?}")]
96 InvalidPositionChar { char: char },
97
98 #[error("[E005] position string length {len} exceeds maximum 64")]
99 PositionTooLong { len: usize },
100
101 #[error("[E005] embedding data length {actual} doesn't match expected {expected} for {dims} dims")]
102 EmbeddingDataMismatch {
103 dims: usize,
104 expected: usize,
105 actual: usize,
106 },
107
108 #[error("[E005] DECIMAL has trailing zeros in mantissa (not normalized)")]
109 DecimalNotNormalized,
110
111 #[error("[E005] DECIMAL mantissa bytes are not minimal")]
112 DecimalMantissaNotMinimal,
113
114 #[error("[E005] float value is NaN")]
115 FloatIsNan,
116
117 #[error("[E005] malformed encoding: {context}")]
118 MalformedEncoding { context: &'static str },
119
120 #[error("[E005] zstd decompression failed: {0}")]
122 DecompressionFailed(String),
123
124 #[error("[E005] decompressed size {actual} doesn't match declared {declared}")]
125 UncompressedSizeMismatch { declared: usize, actual: usize },
126
127 #[error("[E005] duplicate ID in {dict} dictionary: {id:?}")]
128 DuplicateDictionaryEntry { dict: &'static str, id: Id },
129}
130
131impl DecodeError {
132 pub fn code(&self) -> ErrorCode {
134 match self {
135 DecodeError::InvalidMagic { .. } | DecodeError::UnsupportedVersion { .. } => {
136 ErrorCode::InvalidMagicOrVersion
137 }
138 DecodeError::IndexOutOfBounds { .. } => ErrorCode::IndexOutOfBounds,
139 DecodeError::InvalidUtf8 { .. } => ErrorCode::InvalidUtf8,
140 _ => ErrorCode::MalformedEncoding,
141 }
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Error)]
147pub enum EncodeError {
148 #[error("{field} length {len} exceeds maximum {max}")]
149 LengthExceedsLimit {
150 field: &'static str,
151 len: usize,
152 max: usize,
153 },
154
155 #[error("embedding data length {data_len} doesn't match {dims} dims for sub-type {sub_type:?}")]
156 EmbeddingDimensionMismatch {
157 sub_type: u8,
158 dims: usize,
159 data_len: usize,
160 },
161
162 #[error("zstd compression failed: {0}")]
163 CompressionFailed(String),
164
165 #[error("DECIMAL value is not normalized (has trailing zeros)")]
166 DecimalNotNormalized,
167
168 #[error("float value is NaN")]
169 FloatIsNan,
170
171 #[error("POINT latitude {lat} out of range [-90, +90]")]
172 LatitudeOutOfRange { lat: f64 },
173
174 #[error("POINT longitude {lon} out of range [-180, +180]")]
175 LongitudeOutOfRange { lon: f64 },
176
177 #[error("position string contains invalid character")]
178 InvalidPositionChar,
179
180 #[error("position string length exceeds maximum 64")]
181 PositionTooLong,
182
183 #[error("DATE string is not valid ISO 8601: {reason}")]
184 InvalidDate { reason: &'static str },
185
186 #[error("batch entity has {actual} values but schema requires {expected}")]
187 BatchEntityValueCountMismatch { expected: usize, actual: usize },
188
189 #[error("invalid input: {context}")]
190 InvalidInput { context: &'static str },
191
192 #[error("duplicate author ID in canonical mode: {id:?}")]
193 DuplicateAuthor { id: Id },
194
195 #[error("duplicate value (property={property:?}, language={language:?}) in canonical mode")]
196 DuplicateValue { property: Id, language: Option<Id> },
197
198 #[error("duplicate unset property (property={property:?}, language={language:?}) in canonical mode")]
199 DuplicateUnset { property: Id, language: Option<Id> },
200}
201
202#[derive(Debug, Clone, PartialEq, Error)]
204pub enum ValidationError {
205 #[error("value type mismatch for property {property:?}: expected {expected:?}")]
206 TypeMismatch { property: Id, expected: DataType },
207
208 #[error("entity {entity:?} is dead (tombstoned)")]
209 EntityIsDead { entity: Id },
210
211 #[error("relation {relation:?} is dead (tombstoned)")]
212 RelationIsDead { relation: Id },
213
214 #[error("property {property:?} not found in schema")]
215 PropertyNotFound { property: Id },
216
217 #[error("data type mismatch for property {property:?}: schema says {schema:?}, edit declares {declared:?}")]
218 DataTypeInconsistent {
219 property: Id,
220 schema: DataType,
221 declared: DataType,
222 },
223}