vegafusion_common/
error.rs

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    /// Append a new context level to the error
109    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    /// Duplicate error. Not a precise Clone because some of the wrapped error types aren't Clone
232    /// These are converted to internal errors
233    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// Conversion to PyO3 error
468#[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}