1use thiserror::Error;
10
11#[derive(Debug, Error, Clone, PartialEq, Eq)]
20#[error("{path}: {message}")]
21pub struct DocumentError {
22 pub path: String,
24 pub message: String,
26}
27
28impl DocumentError {
29 pub fn new(path: impl Into<String>, message: impl Into<String>) -> Self {
31 Self {
32 path: path.into(),
33 message: message.into(),
34 }
35 }
36}
37
38#[derive(Debug, Error, Clone, PartialEq, Eq)]
46#[error("{message}")]
47pub struct SchemaError {
48 pub path: String,
50 pub code: String,
52 pub message: String,
54}
55
56impl SchemaError {
57 pub fn new(
59 path: impl Into<String>,
60 code: impl Into<String>,
61 message: impl Into<String>,
62 ) -> Self {
63 Self {
64 path: path.into(),
65 code: code.into(),
66 message: message.into(),
67 }
68 }
69}
70
71#[derive(Debug, Error, Clone, PartialEq, Eq)]
76#[error("line {line}, col {col}: {message}")]
77pub struct ParseError {
78 pub line: usize,
80 pub col: usize,
82 pub message: String,
84}
85
86impl ParseError {
87 pub fn new(line: usize, col: usize, message: impl Into<String>) -> Self {
89 Self {
90 line,
91 col,
92 message: message.into(),
93 }
94 }
95}
96
97#[derive(Debug, Error, Clone, PartialEq, Eq)]
107#[error("{0}")]
108pub struct FormatError(pub String);
109
110impl FormatError {
111 pub fn new(message: impl Into<String>) -> Self {
113 Self(message.into())
114 }
115}
116
117#[derive(Debug, Error, Clone, PartialEq, Eq)]
127#[error("{message}")]
128pub struct WriteError {
129 pub message: String,
131 pub report: Option<crate::report::WriteReport>,
133}
134
135impl WriteError {
136 pub fn new(message: impl Into<String>) -> Self {
138 Self {
139 message: message.into(),
140 report: None,
141 }
142 }
143
144 pub fn with_report(message: impl Into<String>, report: crate::report::WriteReport) -> Self {
147 Self {
148 message: message.into(),
149 report: Some(report),
150 }
151 }
152
153 pub fn report(&self) -> Option<&crate::report::WriteReport> {
156 self.report.as_ref()
157 }
158}
159
160impl From<DocumentError> for WriteError {
161 fn from(e: DocumentError) -> Self {
162 WriteError::new(e.message)
163 }
164}
165
166#[derive(Debug, Error, Clone, PartialEq, Eq)]
175#[error("{0}")]
176pub struct MaterializeError(pub crate::schema::ValidationResult);
177
178impl MaterializeError {
179 pub fn new(result: crate::schema::ValidationResult) -> Self {
181 Self(result)
182 }
183
184 pub fn result(&self) -> &crate::schema::ValidationResult {
186 &self.0
187 }
188
189 pub fn errors(&self) -> &[crate::schema::ValidationError] {
191 self.0.errors()
192 }
193}
194
195#[derive(Debug, Error, Clone, PartialEq, Eq)]
197pub enum OmnistError {
198 #[error(transparent)]
200 Document(#[from] DocumentError),
201 #[error(transparent)]
203 Schema(#[from] SchemaError),
204 #[error(transparent)]
206 Materialize(#[from] MaterializeError),
207 #[error(transparent)]
209 Parse(#[from] ParseError),
210 #[error(transparent)]
212 Write(#[from] WriteError),
213 #[error(transparent)]
215 Format(#[from] FormatError),
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn document_error_display_includes_path_and_message() {
224 let e = DocumentError::new("$.foo", "not a Document value");
225 assert_eq!(e.to_string(), "$.foo: not a Document value");
226 }
227
228 #[test]
229 fn omnist_error_wraps_document_error_transparently() {
230 let doc_err = DocumentError::new("$.foo", "boom");
231 let wrapped: OmnistError = doc_err.clone().into();
232 assert_eq!(wrapped.to_string(), doc_err.to_string());
233 assert!(matches!(wrapped, OmnistError::Document(ref inner) if *inner == doc_err));
234 }
235
236 #[test]
237 fn document_error_clone_and_eq() {
238 let a = DocumentError::new("$", "x");
239 let b = a.clone();
240 assert_eq!(a, b);
241 }
242
243 #[test]
244 fn schema_error_display_and_eq() {
245 let e = SchemaError::new("R.a", "schema.unknown-type", "unknown type 'Missing'");
246 assert_eq!(e.path, "R.a");
247 assert_eq!(e.code, "schema.unknown-type");
248 assert_eq!(e.message, "unknown type 'Missing'");
249 assert_eq!(e.to_string(), "unknown type 'Missing'");
250 assert_eq!(e.clone(), e);
251 }
252
253 #[test]
254 fn omnist_error_wraps_schema_error_transparently() {
255 let schema_err = SchemaError::new("$", "schema.syntax", "boom");
256 let wrapped: OmnistError = schema_err.clone().into();
257 assert_eq!(wrapped.to_string(), schema_err.to_string());
258 assert!(matches!(wrapped, OmnistError::Schema(ref inner) if *inner == schema_err));
259 }
260
261 #[test]
262 fn parse_error_display_includes_line_col_and_message() {
263 let e = ParseError::new(3, 7, "stray character '@'");
264 assert_eq!(e.to_string(), "line 3, col 7: stray character '@'");
265 }
266
267 #[test]
268 fn omnist_error_wraps_parse_error_transparently() {
269 let e = ParseError::new(1, 1, "boom");
270 let wrapped: OmnistError = e.clone().into();
271 assert_eq!(wrapped.to_string(), e.to_string());
272 assert!(matches!(wrapped, OmnistError::Parse(ref inner) if *inner == e));
273 }
274
275 #[test]
276 fn write_error_display_and_from_document_error() {
277 let e = WriteError::new("nesting exceeds the maximum depth (200)");
278 assert_eq!(e.to_string(), "nesting exceeds the maximum depth (200)");
279 let doc_err = DocumentError::new("$", "nesting exceeds the maximum depth (200)");
280 let from_doc: WriteError = doc_err.into();
281 assert_eq!(from_doc, e);
282 }
283
284 #[test]
285 fn omnist_error_wraps_write_error_transparently() {
286 let e = WriteError::new("boom");
287 let wrapped: OmnistError = e.clone().into();
288 assert_eq!(wrapped.to_string(), e.to_string());
289 assert!(matches!(wrapped, OmnistError::Write(ref inner) if *inner == e));
290 }
291
292 #[test]
293 fn materialize_error_new_result_and_errors_accessors() {
294 let fields = vec![crate::schema::Field::required("x", crate::schema::STRING).unwrap()];
295 let rec = crate::schema::Record::new(fields).unwrap();
296 let mut env: indexmap::IndexMap<String, crate::schema::Record> = indexmap::IndexMap::new();
297 env.insert("Root".to_string(), rec);
298 let schema = crate::schema::Schema::new(crate::schema::Ref::new("Root"), env).unwrap();
299 let node = crate::document::RawNode::Edges(vec![]);
303 let res = crate::materialize::materialize(&node, Some(&schema))
304 .unwrap_err()
305 .0;
306 assert!(!res.ok());
307
308 let e = MaterializeError::new(res.clone());
309 assert_eq!(e.result(), &res);
310 assert_eq!(e.errors(), res.errors());
311 assert_eq!(e.to_string(), res.to_string());
312 }
313
314 #[test]
315 fn omnist_error_wraps_materialize_error_transparently() {
316 let res = crate::schema::ValidationResult::new();
317 let e = MaterializeError::new(res);
318 let wrapped: OmnistError = e.clone().into();
319 assert_eq!(wrapped.to_string(), e.to_string());
320 assert!(matches!(wrapped, OmnistError::Materialize(ref inner) if *inner == e));
321 }
322}