sparesults/
error.rs

1use json_event_parser::{JsonParseError, JsonSyntaxError};
2use oxrdf::TermParseError;
3use quick_xml::encoding::EncodingError;
4use std::io;
5use std::ops::Range;
6use std::sync::Arc;
7
8/// Error returned during SPARQL result formats format parsing.
9#[derive(Debug, thiserror::Error)]
10pub enum QueryResultsParseError {
11    /// I/O error during parsing (file not found...).
12    #[error(transparent)]
13    Io(#[from] io::Error),
14    /// An error in the file syntax.
15    #[error(transparent)]
16    Syntax(#[from] QueryResultsSyntaxError),
17}
18
19impl From<QueryResultsParseError> for io::Error {
20    #[inline]
21    fn from(error: QueryResultsParseError) -> Self {
22        match error {
23            QueryResultsParseError::Io(error) => error,
24            QueryResultsParseError::Syntax(error) => error.into(),
25        }
26    }
27}
28
29#[doc(hidden)]
30impl From<JsonParseError> for QueryResultsParseError {
31    fn from(error: JsonParseError) -> Self {
32        match error {
33            JsonParseError::Syntax(error) => QueryResultsSyntaxError::from(error).into(),
34            JsonParseError::Io(error) => error.into(),
35        }
36    }
37}
38
39#[doc(hidden)]
40impl From<quick_xml::Error> for QueryResultsParseError {
41    #[inline]
42    fn from(error: quick_xml::Error) -> Self {
43        match error {
44            quick_xml::Error::Io(error) => {
45                Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e)))
46            }
47            _ => Self::Syntax(QueryResultsSyntaxError(SyntaxErrorKind::Xml(error))),
48        }
49    }
50}
51
52#[doc(hidden)]
53impl From<quick_xml::escape::EscapeError> for QueryResultsParseError {
54    #[inline]
55    fn from(error: quick_xml::escape::EscapeError) -> Self {
56        quick_xml::Error::from(error).into()
57    }
58}
59/// An error in the syntax of the parsed file.
60#[derive(Debug, thiserror::Error)]
61#[error(transparent)]
62pub struct QueryResultsSyntaxError(#[from] SyntaxErrorKind);
63
64#[derive(Debug, thiserror::Error)]
65enum SyntaxErrorKind {
66    #[error(transparent)]
67    Json(#[from] JsonSyntaxError),
68    #[error(transparent)]
69    Xml(#[from] quick_xml::Error),
70    #[error("Error {error} on '{term}' in line {}", location.start.line + 1)]
71    Term {
72        #[source]
73        error: TermParseError,
74        term: String,
75        location: Range<TextPosition>,
76    },
77    #[error("{msg}")]
78    Msg {
79        msg: String,
80        location: Option<Range<TextPosition>>,
81    },
82}
83
84impl QueryResultsSyntaxError {
85    /// Builds an error from a printable error message.
86    pub(crate) fn msg(msg: impl Into<String>) -> Self {
87        Self(SyntaxErrorKind::Msg {
88            msg: msg.into(),
89            location: None,
90        })
91    }
92
93    pub(crate) fn term(error: TermParseError, term: String, location: Range<TextPosition>) -> Self {
94        Self(SyntaxErrorKind::Term {
95            error,
96            term,
97            location,
98        })
99    }
100
101    /// Builds an error from a printable error message and a location
102    #[inline]
103    pub(crate) fn located_message(msg: impl Into<String>, location: Range<TextPosition>) -> Self {
104        Self(SyntaxErrorKind::Msg {
105            msg: msg.into(),
106            location: Some(location),
107        })
108    }
109
110    /// The location of the error inside of the file.
111    #[inline]
112    pub fn location(&self) -> Option<Range<TextPosition>> {
113        match &self.0 {
114            SyntaxErrorKind::Json(e) => {
115                let location = e.location();
116                Some(
117                    TextPosition {
118                        line: location.start.line,
119                        column: location.start.column,
120                        offset: location.start.offset,
121                    }..TextPosition {
122                        line: location.end.line,
123                        column: location.end.column,
124                        offset: location.end.offset,
125                    },
126                )
127            }
128            SyntaxErrorKind::Term { location, .. } => Some(location.clone()),
129            SyntaxErrorKind::Msg { location, .. } => location.clone(),
130            SyntaxErrorKind::Xml(_) => None,
131        }
132    }
133}
134
135impl From<QueryResultsSyntaxError> for io::Error {
136    #[inline]
137    fn from(error: QueryResultsSyntaxError) -> Self {
138        match error.0 {
139            SyntaxErrorKind::Json(error) => Self::new(io::ErrorKind::InvalidData, error),
140            SyntaxErrorKind::Xml(error) => match error {
141                quick_xml::Error::Io(error) => {
142                    Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e))
143                }
144                _ => Self::new(io::ErrorKind::InvalidData, error),
145            },
146            SyntaxErrorKind::Term { .. } => Self::new(io::ErrorKind::InvalidData, error),
147            SyntaxErrorKind::Msg { msg, .. } => Self::new(io::ErrorKind::InvalidData, msg),
148        }
149    }
150}
151
152#[doc(hidden)]
153impl From<JsonSyntaxError> for QueryResultsSyntaxError {
154    fn from(error: JsonSyntaxError) -> Self {
155        Self(SyntaxErrorKind::Json(error))
156    }
157}
158
159#[doc(hidden)]
160impl From<EncodingError> for QueryResultsParseError {
161    fn from(error: EncodingError) -> Self {
162        quick_xml::Error::from(error).into()
163    }
164}
165
166/// A position in a text i.e. a `line` number starting from 0, a `column` number starting from 0 (in number of code points) and a global file `offset` starting from 0 (in number of bytes).
167#[derive(Eq, PartialEq, Debug, Clone, Copy)]
168pub struct TextPosition {
169    pub line: u64,
170    pub column: u64,
171    pub offset: u64,
172}