1use arrow::error::ArrowError;
2use datafusion_common::DataFusionError;
3use std::num::ParseFloatError;
4use std::result;
5use thiserror::Error;
6
7#[cfg(feature = "proto")]
8use {datafusion_proto_common::to_proto::Error as DataFusionProtoError, prost::DecodeError};
9
10#[cfg(feature = "pyo3")]
11use pyo3::{exceptions::PyValueError, PyErr};
12
13#[cfg(feature = "base64")]
14use base64::DecodeError as Base64DecodeError;
15
16#[cfg(feature = "object_store")]
17use object_store::{path::Error as ObjectStorePathError, Error as ObjectStoreError};
18
19#[cfg(feature = "url")]
20use url::ParseError as UrlParseError;
21
22pub type Result<T> = result::Result<T, VegaFusionError>;
23
24#[derive(Clone, Debug, Default)]
25pub struct ErrorContext {
26 pub contexts: Vec<String>,
27}
28
29impl std::fmt::Display for ErrorContext {
30 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
31 for (i, context) in self.contexts.iter().enumerate() {
32 writeln!(f, " Context[{i}]: {context}")?;
33 }
34
35 Ok(())
36 }
37}
38
39#[derive(Error, Debug)]
40pub enum VegaFusionError {
41 #[error("Expression parsing error: {0}\n{1}")]
42 ParseError(String, ErrorContext),
43
44 #[error("Expression compilation error: {0}\n{1}")]
45 CompilationError(String, ErrorContext),
46
47 #[error("Internal error: {0}\n{1}")]
48 InternalError(String, ErrorContext),
49
50 #[error("External error: {0}\n{1}")]
51 ExternalError(String, ErrorContext),
52
53 #[error("Vega Specification error: {0}\n{1}")]
54 SpecificationError(String, ErrorContext),
55
56 #[error("Pre-transform error: {0}\n{1}")]
57 PreTransformError(String, ErrorContext),
58
59 #[error("SQL Not Supported error: {0}\n{1}")]
60 SqlNotSupported(String, ErrorContext),
61
62 #[error("Arrow error: {0}\n{1}")]
63 FormatError(std::fmt::Error, ErrorContext),
64
65 #[error("Arrow error: {0}\n{1}")]
66 ArrowError(ArrowError, ErrorContext),
67
68 #[error("DataFusion error: {0}\n{1}")]
69 DataFusionError(DataFusionError, ErrorContext),
70
71 #[cfg(feature = "proto")]
72 #[error("DataFusion proto error: {0}\n{1}")]
73 DataFusionProtoError(DataFusionProtoError, ErrorContext),
74
75 #[cfg(feature = "proto")]
76 #[error("Prost decode error: {0}\n{1}")]
77 ProstDecodeError(DecodeError, ErrorContext),
78
79 #[error("IO Error: {0}\n{1}")]
80 IOError(std::io::Error, ErrorContext),
81
82 #[cfg(feature = "pyo3")]
83 #[error("Python Error: {0}\n{1}")]
84 PythonError(pyo3::PyErr, ErrorContext),
85
86 #[cfg(feature = "json")]
87 #[error("Serde JSON Error: {0}\n{1}")]
88 SerdeJsonError(serde_json::Error, ErrorContext),
89
90 #[cfg(feature = "sqlparser")]
91 #[error("SqlParser Error: {0}\n{1}")]
92 SqlParserError(sqlparser::parser::ParserError, ErrorContext),
93
94 #[cfg(feature = "base64")]
95 #[error("Base64 Decode Error: {0}\n{1}")]
96 Base64DecodeError(Base64DecodeError, ErrorContext),
97
98 #[cfg(feature = "object_store")]
99 #[error("ObjectStoreError Error: {0}\n{1}")]
100 ObjectStoreError(ObjectStoreError, ErrorContext),
101
102 #[cfg(feature = "url")]
103 #[error("url::ParseError Error: {0}\n{1}")]
104 UrlParseError(UrlParseError, ErrorContext),
105}
106
107impl VegaFusionError {
108 pub fn with_context<S, F>(self, context_fn: F) -> Self
110 where
111 F: FnOnce() -> S,
112 S: Into<String>,
113 {
114 use VegaFusionError::*;
115 match self {
116 ParseError(msg, mut context) => {
117 context.contexts.push(context_fn().into());
118 VegaFusionError::ParseError(msg, context)
119 }
120 CompilationError(msg, mut context) => {
121 context.contexts.push(context_fn().into());
122 VegaFusionError::CompilationError(msg, context)
123 }
124 InternalError(msg, mut context) => {
125 context.contexts.push(context_fn().into());
126 VegaFusionError::InternalError(msg, context)
127 }
128 ExternalError(msg, mut context) => {
129 context.contexts.push(context_fn().into());
130 VegaFusionError::ExternalError(msg, context)
131 }
132 SpecificationError(msg, mut context) => {
133 context.contexts.push(context_fn().into());
134 VegaFusionError::SpecificationError(msg, context)
135 }
136 PreTransformError(msg, mut context) => {
137 context.contexts.push(context_fn().into());
138 VegaFusionError::PreTransformError(msg, context)
139 }
140 SqlNotSupported(msg, mut context) => {
141 context.contexts.push(context_fn().into());
142 VegaFusionError::SqlNotSupported(msg, context)
143 }
144 FormatError(msg, mut context) => {
145 context.contexts.push(context_fn().into());
146 VegaFusionError::FormatError(msg, context)
147 }
148 ArrowError(msg, mut context) => {
149 context.contexts.push(context_fn().into());
150 VegaFusionError::ArrowError(msg, context)
151 }
152 DataFusionError(err, mut context) => {
153 context.contexts.push(context_fn().into());
154 VegaFusionError::DataFusionError(err, context)
155 }
156 #[cfg(feature = "proto")]
157 DataFusionProtoError(err, mut context) => {
158 context.contexts.push(context_fn().into());
159 VegaFusionError::DataFusionProtoError(err, context)
160 }
161 #[cfg(feature = "proto")]
162 ProstDecodeError(err, mut context) => {
163 context.contexts.push(context_fn().into());
164 VegaFusionError::ProstDecodeError(err.clone(), context)
165 }
166 IOError(err, mut context) => {
167 context.contexts.push(context_fn().into());
168 VegaFusionError::IOError(err, context)
169 }
170 #[cfg(feature = "pyo3")]
171 PythonError(err, mut context) => {
172 context.contexts.push(context_fn().into());
173 VegaFusionError::PythonError(err, context)
174 }
175 #[cfg(feature = "json")]
176 SerdeJsonError(err, mut context) => {
177 context.contexts.push(context_fn().into());
178 VegaFusionError::SerdeJsonError(err, context)
179 }
180 #[cfg(feature = "sqlparser")]
181 SqlParserError(err, mut context) => {
182 context.contexts.push(context_fn().into());
183 VegaFusionError::SqlParserError(err, context)
184 }
185 #[cfg(feature = "base64")]
186 Base64DecodeError(err, mut context) => {
187 context.contexts.push(context_fn().into());
188 VegaFusionError::Base64DecodeError(err, context)
189 }
190 #[cfg(feature = "object_store")]
191 ObjectStoreError(err, mut context) => {
192 context.contexts.push(context_fn().into());
193 VegaFusionError::ObjectStoreError(err, context)
194 }
195 #[cfg(feature = "url")]
196 UrlParseError(err, mut context) => {
197 context.contexts.push(context_fn().into());
198 VegaFusionError::UrlParseError(err, context)
199 }
200 }
201 }
202
203 pub fn parse<S: Into<String>>(message: S) -> Self {
204 Self::ParseError(message.into(), Default::default())
205 }
206
207 pub fn compilation<S: Into<String>>(message: S) -> Self {
208 Self::CompilationError(message.into(), Default::default())
209 }
210
211 pub fn internal<S: Into<String>>(message: S) -> Self {
212 Self::InternalError(message.into(), Default::default())
213 }
214
215 pub fn external<S: Into<String>>(message: S) -> Self {
216 Self::ExternalError(message.into(), Default::default())
217 }
218
219 pub fn specification<S: Into<String>>(message: S) -> Self {
220 Self::SpecificationError(message.into(), Default::default())
221 }
222
223 pub fn pre_transform<S: Into<String>>(message: S) -> Self {
224 Self::PreTransformError(message.into(), Default::default())
225 }
226
227 pub fn sql_not_supported<S: Into<String>>(message: S) -> Self {
228 Self::SqlNotSupported(message.into(), Default::default())
229 }
230
231 pub fn duplicate(&self) -> Self {
234 use VegaFusionError::*;
235 match self {
236 ParseError(msg, context) => VegaFusionError::ParseError(msg.clone(), context.clone()),
237 CompilationError(msg, context) => {
238 VegaFusionError::CompilationError(msg.clone(), context.clone())
239 }
240 InternalError(msg, context) => {
241 VegaFusionError::InternalError(msg.clone(), context.clone())
242 }
243 ExternalError(msg, context) => {
244 VegaFusionError::ExternalError(msg.clone(), context.clone())
245 }
246 SpecificationError(msg, context) => {
247 VegaFusionError::SpecificationError(msg.clone(), context.clone())
248 }
249 PreTransformError(msg, context) => {
250 VegaFusionError::PreTransformError(msg.clone(), context.clone())
251 }
252 SqlNotSupported(msg, context) => {
253 VegaFusionError::SqlNotSupported(msg.clone(), context.clone())
254 }
255 FormatError(err, context) => VegaFusionError::FormatError(*err, context.clone()),
256 ArrowError(err, context) => {
257 VegaFusionError::ExternalError(err.to_string(), context.clone())
258 }
259 DataFusionError(err, context) => {
260 VegaFusionError::ExternalError(err.to_string(), context.clone())
261 }
262 #[cfg(feature = "proto")]
263 DataFusionProtoError(err, context) => {
264 VegaFusionError::ExternalError(err.to_string(), context.clone())
265 }
266 #[cfg(feature = "proto")]
267 ProstDecodeError(err, context) => {
268 VegaFusionError::ProstDecodeError(err.clone(), context.clone())
269 }
270 IOError(err, context) => {
271 VegaFusionError::ExternalError(err.to_string(), context.clone())
272 }
273 #[cfg(feature = "pyo3")]
274 PythonError(msg, context) => {
275 VegaFusionError::ExternalError(msg.to_string(), context.clone())
276 }
277 #[cfg(feature = "json")]
278 SerdeJsonError(err, context) => {
279 VegaFusionError::ExternalError(err.to_string(), context.clone())
280 }
281 #[cfg(feature = "sqlparser")]
282 SqlParserError(err, context) => {
283 VegaFusionError::SqlParserError(err.clone(), context.clone())
284 }
285 #[cfg(feature = "base64")]
286 Base64DecodeError(err, context) => {
287 VegaFusionError::Base64DecodeError(err.clone(), context.clone())
288 }
289 #[cfg(feature = "object_store")]
290 ObjectStoreError(err, context) => {
291 VegaFusionError::ExternalError(err.to_string(), context.clone())
292 }
293 #[cfg(feature = "url")]
294 UrlParseError(err, context) => VegaFusionError::UrlParseError(*err, context.clone()),
295 }
296 }
297}
298
299pub trait ResultWithContext<R> {
300 fn with_context<S, F>(self, context_fn: F) -> Result<R>
301 where
302 F: FnOnce() -> S,
303 S: Into<String>;
304}
305
306impl<R, E> ResultWithContext<R> for result::Result<R, E>
307where
308 E: Into<VegaFusionError>,
309{
310 fn with_context<S, F>(self, context_fn: F) -> Result<R>
311 where
312 F: FnOnce() -> S,
313 S: Into<String>,
314 {
315 match self {
316 Ok(val) => Ok(val),
317 Err(err) => {
318 let vega_fusion_error: VegaFusionError = err.into();
319 Err(vega_fusion_error.with_context(context_fn))
320 }
321 }
322 }
323}
324
325impl<R> ResultWithContext<R> for Option<R> {
326 fn with_context<S, F>(self, context_fn: F) -> Result<R>
327 where
328 F: FnOnce() -> S,
329 S: Into<String>,
330 {
331 match self {
332 Some(val) => Ok(val),
333 None => Err(VegaFusionError::internal(context_fn().into())),
334 }
335 }
336}
337
338impl From<ParseFloatError> for VegaFusionError {
339 fn from(err: ParseFloatError) -> Self {
340 Self::parse(err.to_string())
341 }
342}
343
344impl From<DataFusionError> for VegaFusionError {
345 fn from(err: DataFusionError) -> Self {
346 Self::DataFusionError(err, Default::default())
347 }
348}
349
350#[cfg(feature = "proto")]
351impl From<DataFusionProtoError> for VegaFusionError {
352 fn from(err: DataFusionProtoError) -> Self {
353 Self::DataFusionProtoError(err, Default::default())
354 }
355}
356
357#[cfg(feature = "proto")]
358impl From<DecodeError> for VegaFusionError {
359 fn from(err: DecodeError) -> Self {
360 Self::ProstDecodeError(err, Default::default())
361 }
362}
363
364impl From<std::fmt::Error> for VegaFusionError {
365 fn from(err: std::fmt::Error) -> Self {
366 Self::FormatError(err, Default::default())
367 }
368}
369
370impl From<ArrowError> for VegaFusionError {
371 fn from(err: ArrowError) -> Self {
372 Self::ArrowError(err, Default::default())
373 }
374}
375
376impl From<std::io::Error> for VegaFusionError {
377 fn from(err: std::io::Error) -> Self {
378 Self::IOError(err, Default::default())
379 }
380}
381
382#[cfg(feature = "pyo3")]
383impl From<PyErr> for VegaFusionError {
384 fn from(err: PyErr) -> Self {
385 Self::PythonError(err, Default::default())
386 }
387}
388
389#[cfg(feature = "json")]
390impl From<serde_json::Error> for VegaFusionError {
391 fn from(err: serde_json::Error) -> Self {
392 Self::SerdeJsonError(err, Default::default())
393 }
394}
395
396#[cfg(feature = "sqlparser")]
397impl From<sqlparser::parser::ParserError> for VegaFusionError {
398 fn from(err: sqlparser::parser::ParserError) -> Self {
399 Self::SqlParserError(err, Default::default())
400 }
401}
402
403#[cfg(feature = "base64")]
404impl From<Base64DecodeError> for VegaFusionError {
405 fn from(err: Base64DecodeError) -> Self {
406 Self::Base64DecodeError(err, Default::default())
407 }
408}
409
410#[cfg(feature = "object_store")]
411impl From<ObjectStoreError> for VegaFusionError {
412 fn from(err: ObjectStoreError) -> Self {
413 Self::ObjectStoreError(err, Default::default())
414 }
415}
416
417#[cfg(feature = "object_store")]
418impl From<ObjectStorePathError> for VegaFusionError {
419 fn from(err: ObjectStorePathError) -> Self {
420 Self::ObjectStoreError(
421 ObjectStoreError::InvalidPath { source: err },
422 Default::default(),
423 )
424 }
425}
426
427#[cfg(feature = "url")]
428impl From<UrlParseError> for VegaFusionError {
429 fn from(err: UrlParseError) -> Self {
430 Self::UrlParseError(err, Default::default())
431 }
432}
433pub trait ToExternalError<T> {
434 fn external<S: Into<String>>(self, context: S) -> Result<T>;
435}
436
437impl<T, E: std::error::Error> ToExternalError<T> for std::result::Result<T, E> {
438 fn external<S: Into<String>>(self, context: S) -> Result<T> {
439 match self {
440 Ok(v) => Ok(v),
441 Err(err) => {
442 let context = ErrorContext {
443 contexts: vec![context.into()],
444 };
445 Err(VegaFusionError::ExternalError(err.to_string(), context))
446 }
447 }
448 }
449}
450
451pub trait DuplicateResult {
452 fn duplicate(&self) -> Self;
453}
454
455impl<T> DuplicateResult for Result<T>
456where
457 T: Clone,
458{
459 fn duplicate(&self) -> Self {
460 match self {
461 Ok(v) => Ok(v.clone()),
462 Err(err) => Err(err.duplicate()),
463 }
464 }
465}
466
467#[cfg(feature = "pyo3")]
469impl std::convert::From<VegaFusionError> for PyErr {
470 fn from(err: VegaFusionError) -> PyErr {
471 PyValueError::new_err(err.to_string())
472 }
473}