Convert all Error + ErrorKind into one error

pull/745/head
Yuri Astrakhan 11 months ago
parent dae629fd4e
commit a62c5102c4
  1. 3
      Cargo.lock
  2. 47
      lib/oxigraph/src/storage/error.rs
  3. 1
      lib/oxrdf/Cargo.toml
  4. 86
      lib/oxrdf/src/parser.rs
  5. 77
      lib/oxrdfio/src/error.rs
  6. 66
      lib/oxrdfxml/src/error.rs
  7. 26
      lib/oxrdfxml/src/parser.rs
  8. 3
      lib/oxsdatatypes/Cargo.toml
  9. 54
      lib/oxsdatatypes/src/date_time.rs
  10. 66
      lib/oxsdatatypes/src/decimal.rs
  11. 34
      lib/sparesults/src/csv.rs
  12. 97
      lib/sparesults/src/error.rs
  13. 1
      lib/spargebra/Cargo.toml
  14. 53
      lib/spargebra/src/parser.rs

3
Cargo.lock generated

@ -1136,6 +1136,7 @@ dependencies = [
"oxiri", "oxiri",
"oxsdatatypes", "oxsdatatypes",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]
@ -1176,6 +1177,7 @@ name = "oxsdatatypes"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.1"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"thiserror",
] ]
[[package]] [[package]]
@ -1761,6 +1763,7 @@ dependencies = [
"oxrdf", "oxrdf",
"peg", "peg",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]

@ -1,7 +1,7 @@
use crate::io::{ParseError, RdfFormat}; use crate::io::{ParseError, RdfFormat};
use oxiri::IriParseError; use oxiri::IriParseError;
use std::error::Error; use std::error::Error;
use std::{fmt, io}; use std::io;
use thiserror::Error; use thiserror::Error;
/// An error related to storage operations (reads, writes...). /// An error related to storage operations (reads, writes...).
@ -15,8 +15,8 @@ pub enum StorageError {
#[error(transparent)] #[error(transparent)]
Corruption(#[from] CorruptionError), Corruption(#[from] CorruptionError),
#[doc(hidden)] #[doc(hidden)]
#[error(transparent)] #[error("{0}")]
Other(Box<dyn Error + Send + Sync + 'static>), Other(#[source] Box<dyn Error + Send + Sync + 'static>),
} }
impl From<StorageError> for io::Error { impl From<StorageError> for io::Error {
@ -31,52 +31,25 @@ impl From<StorageError> for io::Error {
} }
/// An error return if some content in the database is corrupted. /// An error return if some content in the database is corrupted.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct CorruptionError { pub enum CorruptionError {
inner: CorruptionErrorKind, #[error("{0}")]
}
#[derive(Debug)]
enum CorruptionErrorKind {
Msg(String), Msg(String),
Other(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
Other(#[source] Box<dyn Error + Send + Sync + 'static>),
} }
impl CorruptionError { impl CorruptionError {
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn new(error: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self { pub(crate) fn new(error: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
Self { Self::Other(error.into())
inner: CorruptionErrorKind::Other(error.into()),
}
} }
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self { Self::Msg(msg.into())
inner: CorruptionErrorKind::Msg(msg.into()),
}
}
}
impl fmt::Display for CorruptionError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
CorruptionErrorKind::Msg(e) => e.fmt(f),
CorruptionErrorKind::Other(e) => e.fmt(f),
}
}
}
impl Error for CorruptionError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.inner {
CorruptionErrorKind::Msg(_) => None,
CorruptionErrorKind::Other(e) => Some(e.as_ref()),
}
} }
} }

@ -22,6 +22,7 @@ oxilangtag.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxsdatatypes = { workspace = true, optional = true } oxsdatatypes = { workspace = true, optional = true }
rand.workspace = true rand.workspace = true
thiserror.workspace = true
[lints] [lints]
workspace = true workspace = true

@ -5,9 +5,9 @@ use crate::{
}; };
#[cfg(feature = "rdf-star")] #[cfg(feature = "rdf-star")]
use crate::{Subject, Triple}; use crate::{Subject, Triple};
use std::error::Error; use std::char;
use std::str::{Chars, FromStr}; use std::str::{Chars, FromStr};
use std::{char, fmt}; use thiserror::Error;
/// This limit is set in order to avoid stack overflow error when parsing nested triples due to too many recursive calls. /// This limit is set in order to avoid stack overflow error when parsing nested triples due to too many recursive calls.
/// The actual limit value is a wet finger compromise between not failing to parse valid files and avoiding to trigger stack overflow errors. /// The actual limit value is a wet finger compromise between not failing to parse valid files and avoiding to trigger stack overflow errors.
@ -166,11 +166,9 @@ impl FromStr for Variable {
"Variable serialization should start with ? or $", "Variable serialization should start with ? or $",
)); ));
} }
Self::new(&s[1..]).map_err(|error| Self::Err { Self::new(&s[1..]).map_err(|error| Self::Err::Variable {
kind: TermParseErrorKind::Variable { value: s.to_owned(),
value: s.to_owned(), error,
error,
},
}) })
} }
} }
@ -183,11 +181,9 @@ fn read_named_node(s: &str) -> Result<(NamedNode, &str), TermParseError> {
.ok_or_else(|| TermParseError::msg("Named node serialization should end with a >"))?; .ok_or_else(|| TermParseError::msg("Named node serialization should end with a >"))?;
let (value, remain) = remain.split_at(end); let (value, remain) = remain.split_at(end);
let remain = &remain[1..]; let remain = &remain[1..];
let term = NamedNode::new(value).map_err(|error| TermParseError { let term = NamedNode::new(value).map_err(|error| TermParseError::Iri {
kind: TermParseErrorKind::Iri { value: value.to_owned(),
value: value.to_owned(), error,
error,
},
})?; })?;
Ok((term, remain)) Ok((term, remain))
} else { } else {
@ -207,11 +203,9 @@ fn read_blank_node(s: &str) -> Result<(BlankNode, &str), TermParseError> {
}) })
.unwrap_or(remain.len()); .unwrap_or(remain.len());
let (value, remain) = remain.split_at(end); let (value, remain) = remain.split_at(end);
let term = BlankNode::new(value).map_err(|error| TermParseError { let term = BlankNode::new(value).map_err(|error| TermParseError::BlankNode {
kind: TermParseErrorKind::BlankNode { value: value.to_owned(),
value: value.to_owned(), error,
error,
},
})?; })?;
Ok((term, remain)) Ok((term, remain))
} else { } else {
@ -237,11 +231,9 @@ fn read_literal(s: &str) -> Result<(Literal, &str), TermParseError> {
let (language, remain) = remain.split_at(end); let (language, remain) = remain.split_at(end);
Ok(( Ok((
Literal::new_language_tagged_literal(value, language).map_err( Literal::new_language_tagged_literal(value, language).map_err(
|error| TermParseError { |error| TermParseError::LanguageTag {
kind: TermParseErrorKind::LanguageTag { value: language.to_owned(),
value: language.to_owned(), error,
error,
},
}, },
)?, )?,
remain, remain,
@ -421,61 +413,31 @@ fn read_hexa_char(input: &mut Chars<'_>, len: usize) -> Result<char, TermParseEr
} }
/// An error raised during term serialization parsing using the [`FromStr`] trait. /// An error raised during term serialization parsing using the [`FromStr`] trait.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct TermParseError { pub enum TermParseError {
kind: TermParseErrorKind, #[error("Error while parsing the named node '{value}': {error}")]
} Iri { error: IriParseError, value: String },
#[error("Error while parsing the blank node '{value}': {error}")]
#[derive(Debug)]
enum TermParseErrorKind {
Iri {
error: IriParseError,
value: String,
},
BlankNode { BlankNode {
error: BlankNodeIdParseError, error: BlankNodeIdParseError,
value: String, value: String,
}, },
#[error("Error while parsing the language tag '{value}': {error}")]
LanguageTag { LanguageTag {
error: LanguageTagParseError, error: LanguageTagParseError,
value: String, value: String,
}, },
#[error("Error while parsing the variable '{value}': {error}")]
Variable { Variable {
error: VariableNameParseError, error: VariableNameParseError,
value: String, value: String,
}, },
Msg { #[error("{msg}")]
msg: &'static str, Msg { msg: &'static str },
},
} }
impl fmt::Display for TermParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
TermParseErrorKind::Iri { error, value } => {
write!(f, "Error while parsing the named node '{value}': {error}")
}
TermParseErrorKind::BlankNode { error, value } => {
write!(f, "Error while parsing the blank node '{value}': {error}")
}
TermParseErrorKind::LanguageTag { error, value } => {
write!(f, "Error while parsing the language tag '{value}': {error}")
}
TermParseErrorKind::Variable { error, value } => {
write!(f, "Error while parsing the variable '{value}': {error}")
}
TermParseErrorKind::Msg { msg } => f.write_str(msg),
}
}
}
impl Error for TermParseError {}
impl TermParseError { impl TermParseError {
pub(crate) fn msg(msg: &'static str) -> Self { pub(crate) fn msg(msg: &'static str) -> Self {
Self { Self::Msg { msg }
kind: TermParseErrorKind::Msg { msg },
}
} }
} }

@ -1,6 +1,5 @@
use std::error::Error; use std::io;
use std::ops::Range; use std::ops::Range;
use std::{fmt, io};
use thiserror::Error; use thiserror::Error;
/// Error returned during RDF format parsing. /// Error returned during RDF format parsing.
@ -16,18 +15,7 @@ pub enum ParseError {
impl ParseError { impl ParseError {
pub(crate) fn msg(msg: &'static str) -> Self { pub(crate) fn msg(msg: &'static str) -> Self {
Self::Syntax(SyntaxError { Self::Syntax(SyntaxError::Msg { msg })
inner: SyntaxErrorKind::Msg { msg },
})
}
}
impl From<oxttl::SyntaxError> for SyntaxError {
#[inline]
fn from(error: oxttl::SyntaxError) -> Self {
Self {
inner: SyntaxErrorKind::Turtle(error),
}
} }
} }
@ -41,15 +29,6 @@ impl From<oxttl::ParseError> for ParseError {
} }
} }
impl From<oxrdfxml::SyntaxError> for SyntaxError {
#[inline]
fn from(error: oxrdfxml::SyntaxError) -> Self {
Self {
inner: SyntaxErrorKind::RdfXml(error),
}
}
}
impl From<oxrdfxml::ParseError> for ParseError { impl From<oxrdfxml::ParseError> for ParseError {
#[inline] #[inline]
fn from(error: oxrdfxml::ParseError) -> Self { fn from(error: oxrdfxml::ParseError) -> Self {
@ -71,15 +50,13 @@ impl From<ParseError> for io::Error {
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct SyntaxError { pub enum SyntaxError {
inner: SyntaxErrorKind, #[error(transparent)]
} Turtle(#[from] oxttl::SyntaxError),
#[error(transparent)]
#[derive(Debug)] RdfXml(#[from] oxrdfxml::SyntaxError),
enum SyntaxErrorKind { #[error("{msg}")]
Turtle(oxttl::SyntaxError),
RdfXml(oxrdfxml::SyntaxError),
Msg { msg: &'static str }, Msg { msg: &'static str },
} }
@ -87,8 +64,8 @@ impl SyntaxError {
/// The location of the error inside of the file. /// The location of the error inside of the file.
#[inline] #[inline]
pub fn location(&self) -> Option<Range<TextPosition>> { pub fn location(&self) -> Option<Range<TextPosition>> {
match &self.inner { match &self {
SyntaxErrorKind::Turtle(e) => { Self::Turtle(e) => {
let location = e.location(); let location = e.location();
Some( Some(
TextPosition { TextPosition {
@ -102,29 +79,7 @@ impl SyntaxError {
}, },
) )
} }
SyntaxErrorKind::RdfXml(_) | SyntaxErrorKind::Msg { .. } => None, Self::RdfXml(_) | Self::Msg { .. } => None,
}
}
}
impl fmt::Display for SyntaxError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
SyntaxErrorKind::Turtle(e) => e.fmt(f),
SyntaxErrorKind::RdfXml(e) => e.fmt(f),
SyntaxErrorKind::Msg { msg } => f.write_str(msg),
}
}
}
impl Error for SyntaxError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.inner {
SyntaxErrorKind::Turtle(e) => Some(e),
SyntaxErrorKind::RdfXml(e) => Some(e),
SyntaxErrorKind::Msg { .. } => None,
} }
} }
} }
@ -132,10 +87,10 @@ impl Error for SyntaxError {
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
match error.inner { match error {
SyntaxErrorKind::Turtle(error) => error.into(), SyntaxError::Turtle(error) => error.into(),
SyntaxErrorKind::RdfXml(error) => error.into(), SyntaxError::RdfXml(error) => error.into(),
SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), SyntaxError::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg),
} }
} }
} }

@ -1,8 +1,7 @@
use oxilangtag::LanguageTagParseError; use oxilangtag::LanguageTagParseError;
use oxiri::IriParseError; use oxiri::IriParseError;
use std::error::Error; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io};
use thiserror::Error; use thiserror::Error;
/// Error returned during RDF/XML parsing. /// Error returned during RDF/XML parsing.
@ -33,78 +32,45 @@ impl From<quick_xml::Error> for ParseError {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e))) Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e)))
} }
_ => Self::Syntax(SyntaxError { _ => Self::Syntax(SyntaxError::Xml(error)),
inner: SyntaxErrorKind::Xml(error),
}),
} }
} }
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct SyntaxError { pub enum SyntaxError {
pub(crate) inner: SyntaxErrorKind, #[error(transparent)]
} Xml(#[from] quick_xml::Error),
#[error("error while parsing IRI '{iri}': {error}")]
#[derive(Debug)]
pub enum SyntaxErrorKind {
Xml(quick_xml::Error),
InvalidIri { InvalidIri {
iri: String, iri: String,
#[source]
error: IriParseError, error: IriParseError,
}, },
#[error("error while parsing language tag '{tag}': {error}")]
InvalidLanguageTag { InvalidLanguageTag {
tag: String, tag: String,
#[source]
error: LanguageTagParseError, error: LanguageTagParseError,
}, },
Msg { #[error("{msg}")]
msg: String, Msg { msg: String },
},
} }
impl SyntaxError { impl SyntaxError {
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self { Self::Msg { msg: msg.into() }
inner: SyntaxErrorKind::Msg { msg: msg.into() },
}
}
}
impl fmt::Display for SyntaxError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
SyntaxErrorKind::Xml(error) => error.fmt(f),
SyntaxErrorKind::InvalidIri { iri, error } => {
write!(f, "error while parsing IRI '{iri}': {error}")
}
SyntaxErrorKind::InvalidLanguageTag { tag, error } => {
write!(f, "error while parsing language tag '{tag}': {error}")
}
SyntaxErrorKind::Msg { msg } => f.write_str(msg),
}
}
}
impl Error for SyntaxError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.inner {
SyntaxErrorKind::Xml(error) => Some(error),
SyntaxErrorKind::InvalidIri { error, .. } => Some(error),
SyntaxErrorKind::InvalidLanguageTag { error, .. } => Some(error),
SyntaxErrorKind::Msg { .. } => None,
}
} }
} }
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
match error.inner { match error {
SyntaxErrorKind::Xml(error) => match error { SyntaxError::Xml(error) => match error {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e))
} }
@ -113,7 +79,7 @@ impl From<SyntaxError> for io::Error {
} }
_ => Self::new(io::ErrorKind::InvalidData, error), _ => Self::new(io::ErrorKind::InvalidData, error),
}, },
SyntaxErrorKind::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg), SyntaxError::Msg { msg } => Self::new(io::ErrorKind::InvalidData, msg),
_ => Self::new(io::ErrorKind::InvalidData, error), _ => Self::new(io::ErrorKind::InvalidData, error),
} }
} }

@ -1,4 +1,4 @@
use crate::error::{ParseError, SyntaxError, SyntaxErrorKind}; use crate::error::{ParseError, SyntaxError};
use crate::utils::*; use crate::utils::*;
use oxilangtag::LanguageTag; use oxilangtag::LanguageTag;
use oxiri::{Iri, IriParseError}; use oxiri::{Iri, IriParseError};
@ -575,9 +575,7 @@ impl<R> RdfXmlReader<R> {
tag tag
} else { } else {
LanguageTag::parse(tag.to_ascii_lowercase()) LanguageTag::parse(tag.to_ascii_lowercase())
.map_err(|error| SyntaxError { .map_err(|error| SyntaxError::InvalidLanguageTag { tag, error })?
inner: SyntaxErrorKind::InvalidLanguageTag { tag, error },
})?
.into_inner() .into_inner()
}); });
} else if attribute.key.as_ref() == b"xml:base" { } else if attribute.key.as_ref() == b"xml:base" {
@ -588,9 +586,7 @@ impl<R> RdfXmlReader<R> {
} else { } else {
Iri::parse(iri.clone()) Iri::parse(iri.clone())
} }
.map_err(|error| SyntaxError { .map_err(|error| SyntaxError::InvalidIri { iri, error })?,
inner: SyntaxErrorKind::InvalidIri { iri, error },
})?,
) )
} else { } else {
// We ignore other xml attributes // We ignore other xml attributes
@ -1169,11 +1165,9 @@ impl<R> RdfXmlReader<R> {
} else { } else {
base_iri.resolve(&relative_iri) base_iri.resolve(&relative_iri)
} }
.map_err(|error| SyntaxError { .map_err(|error| SyntaxError::InvalidIri {
inner: SyntaxErrorKind::InvalidIri { iri: relative_iri,
iri: relative_iri, error,
error,
},
})? })?
.into_inner(), .into_inner(),
)) ))
@ -1187,11 +1181,9 @@ impl<R> RdfXmlReader<R> {
relative_iri relative_iri
} else { } else {
Iri::parse(relative_iri.clone()) Iri::parse(relative_iri.clone())
.map_err(|error| SyntaxError { .map_err(|error| SyntaxError::InvalidIri {
inner: SyntaxErrorKind::InvalidIri { iri: relative_iri,
iri: relative_iri, error,
error,
},
})? })?
.into_inner() .into_inner()
})) }))

@ -13,6 +13,9 @@ documentation = "https://docs.rs/oxsdatatypes"
edition.workspace = true edition.workspace = true
rust-version.workspace = true rust-version.workspace = true
[dependencies]
thiserror.workspace = true
[features] [features]
js = ["js-sys"] js = ["js-sys"]
custom-now = [] custom-now = []

@ -6,6 +6,7 @@ use std::error::Error;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::str::FromStr; use std::str::FromStr;
use thiserror::Error;
/// [XML Schema `dateTime` datatype](https://www.w3.org/TR/xmlschema11-2/#dateTime) /// [XML Schema `dateTime` datatype](https://www.w3.org/TR/xmlschema11-2/#dateTime)
/// ///
@ -2039,42 +2040,24 @@ fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option<Decimal> {
} }
/// A parsing error /// A parsing error
#[derive(Debug, Clone)] #[derive(Debug, Clone, Error)]
pub struct ParseDateTimeError { pub enum ParseDateTimeError {
kind: ParseDateTimeErrorKind, #[error("{day} is not a valid day of {month}")]
}
#[derive(Debug, Clone)]
enum ParseDateTimeErrorKind {
InvalidDayOfMonth { day: u8, month: u8 }, InvalidDayOfMonth { day: u8, month: u8 },
Overflow(DateTimeOverflowError), #[error(transparent)]
Overflow(#[from] DateTimeOverflowError),
#[error(transparent)]
InvalidTimezone(InvalidTimezoneError), InvalidTimezone(InvalidTimezoneError),
#[error("{0}")]
Message(&'static str), Message(&'static str),
} }
impl fmt::Display for ParseDateTimeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ParseDateTimeErrorKind::InvalidDayOfMonth { day, month } => {
write!(f, "{day} is not a valid day of {month}")
}
ParseDateTimeErrorKind::Overflow(error) => error.fmt(f),
ParseDateTimeErrorKind::InvalidTimezone(error) => error.fmt(f),
ParseDateTimeErrorKind::Message(msg) => f.write_str(msg),
}
}
}
impl ParseDateTimeError { impl ParseDateTimeError {
const fn msg(message: &'static str) -> Self { const fn msg(message: &'static str) -> Self {
Self { Self::Message(message)
kind: ParseDateTimeErrorKind::Message(message),
}
} }
} }
impl Error for ParseDateTimeError {}
// [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag? // [16] dateTimeLexicalRep ::= yearFrag '-' monthFrag '-' dayFrag 'T' ((hourFrag ':' minuteFrag ':' secondFrag) | endOfDayFrag) timezoneFrag?
fn date_time_lexical_rep(input: &str) -> Result<(DateTime, &str), ParseDateTimeError> { fn date_time_lexical_rep(input: &str) -> Result<(DateTime, &str), ParseDateTimeError> {
let (year, input) = year_frag(input)?; let (year, input) = year_frag(input)?;
@ -2326,11 +2309,8 @@ fn timezone_frag(input: &str) -> Result<(TimezoneOffset, &str), ParseDateTimeErr
} }
Ok(( Ok((
TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes))).map_err(|e| { TimezoneOffset::new(sign * (hours * 60 + i16::from(minutes)))
ParseDateTimeError { .map_err(|e| ParseDateTimeError::InvalidTimezone(e))?,
kind: ParseDateTimeErrorKind::InvalidTimezone(e),
}
})?,
input, input,
)) ))
} }
@ -2400,9 +2380,7 @@ fn optional_end<T>(
fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), ParseDateTimeError> { fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), ParseDateTimeError> {
// Constraint: Day-of-month Values // Constraint: Day-of-month Values
if day > days_in_month(year, month) { if day > days_in_month(year, month) {
return Err(ParseDateTimeError { return Err(ParseDateTimeError::InvalidDayOfMonth { day, month });
kind: ParseDateTimeErrorKind::InvalidDayOfMonth { day, month },
});
} }
Ok(()) Ok(())
} }
@ -2421,14 +2399,6 @@ impl fmt::Display for DateTimeOverflowError {
impl Error for DateTimeOverflowError {} impl Error for DateTimeOverflowError {}
impl From<DateTimeOverflowError> for ParseDateTimeError {
fn from(error: DateTimeOverflowError) -> Self {
Self {
kind: ParseDateTimeErrorKind::Overflow(error),
}
}
}
/// The value provided as timezone is not valid. /// The value provided as timezone is not valid.
/// ///
/// Matches XPath [`FODT0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0003). /// Matches XPath [`FODT0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0003).

@ -3,6 +3,7 @@ use std::error::Error;
use std::fmt; use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use std::str::FromStr; use std::str::FromStr;
use thiserror::Error;
const DECIMAL_PART_DIGITS: u32 = 18; const DECIMAL_PART_DIGITS: u32 = 18;
const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000; const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000;
@ -466,7 +467,7 @@ impl FromStr for Decimal {
// (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+) // (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)
let input = input.as_bytes(); let input = input.as_bytes();
if input.is_empty() { if input.is_empty() {
return Err(PARSE_UNEXPECTED_END); return Err(ParseDecimalError::UnexpectedEnd);
} }
let (sign, mut input) = match input.first() { let (sign, mut input) = match input.first() {
@ -481,9 +482,9 @@ impl FromStr for Decimal {
if c.is_ascii_digit() { if c.is_ascii_digit() {
value = value value = value
.checked_mul(10) .checked_mul(10)
.ok_or(PARSE_OVERFLOW)? .ok_or(ParseDecimalError::Overflow)?
.checked_add(sign * i128::from(*c - b'0')) .checked_add(sign * i128::from(*c - b'0'))
.ok_or(PARSE_OVERFLOW)?; .ok_or(ParseDecimalError::Overflow)?;
input = &input[1..]; input = &input[1..];
} else { } else {
break; break;
@ -493,12 +494,12 @@ impl FromStr for Decimal {
let mut exp = DECIMAL_PART_POW; let mut exp = DECIMAL_PART_POW;
if let Some(c) = input.first() { if let Some(c) = input.first() {
if *c != b'.' { if *c != b'.' {
return Err(PARSE_UNEXPECTED_CHAR); return Err(ParseDecimalError::UnexpectedChar);
} }
input = &input[1..]; input = &input[1..];
if input.is_empty() && !with_before_dot { if input.is_empty() && !with_before_dot {
// We only have a dot // We only have a dot
return Err(PARSE_UNEXPECTED_END); return Err(ParseDecimalError::UnexpectedEnd);
} }
while input.last() == Some(&b'0') { while input.last() == Some(&b'0') {
// Hack to avoid underflows // Hack to avoid underflows
@ -509,25 +510,25 @@ impl FromStr for Decimal {
exp /= 10; exp /= 10;
value = value value = value
.checked_mul(10) .checked_mul(10)
.ok_or(PARSE_OVERFLOW)? .ok_or(ParseDecimalError::Overflow)?
.checked_add(sign * i128::from(*c - b'0')) .checked_add(sign * i128::from(*c - b'0'))
.ok_or(PARSE_OVERFLOW)?; .ok_or(ParseDecimalError::Overflow)?;
input = &input[1..]; input = &input[1..];
} else { } else {
return Err(PARSE_UNEXPECTED_CHAR); return Err(ParseDecimalError::UnexpectedChar);
} }
} }
if exp == 0 { if exp == 0 {
// Underflow // Underflow
return Err(PARSE_UNDERFLOW); return Err(ParseDecimalError::Underflow);
} }
} else if !with_before_dot { } else if !with_before_dot {
// It's empty // It's empty
return Err(PARSE_UNEXPECTED_END); return Err(ParseDecimalError::UnexpectedEnd);
} }
Ok(Self { Ok(Self {
value: value.checked_mul(exp).ok_or(PARSE_OVERFLOW)?, value: value.checked_mul(exp).ok_or(ParseDecimalError::Overflow)?,
}) })
} }
} }
@ -609,50 +610,21 @@ impl fmt::Display for Decimal {
} }
/// An error when parsing a [`Decimal`]. /// An error when parsing a [`Decimal`].
#[derive(Debug, Clone)] #[derive(Debug, Clone, Error)]
pub struct ParseDecimalError { pub enum ParseDecimalError {
kind: DecimalParseErrorKind, #[error("Value overflow")]
}
#[derive(Debug, Clone)]
enum DecimalParseErrorKind {
Overflow, Overflow,
#[error("Value underflow")]
Underflow, Underflow,
#[error("Unexpected character")]
UnexpectedChar, UnexpectedChar,
#[error("Unexpected end of string")]
UnexpectedEnd, UnexpectedEnd,
} }
const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError {
kind: DecimalParseErrorKind::Overflow,
};
const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError {
kind: DecimalParseErrorKind::Underflow,
};
const PARSE_UNEXPECTED_CHAR: ParseDecimalError = ParseDecimalError {
kind: DecimalParseErrorKind::UnexpectedChar,
};
const PARSE_UNEXPECTED_END: ParseDecimalError = ParseDecimalError {
kind: DecimalParseErrorKind::UnexpectedEnd,
};
impl fmt::Display for ParseDecimalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
DecimalParseErrorKind::Overflow => f.write_str("Value overflow"),
DecimalParseErrorKind::Underflow => f.write_str("Value underflow"),
DecimalParseErrorKind::UnexpectedChar => f.write_str("Unexpected character"),
DecimalParseErrorKind::UnexpectedEnd => f.write_str("Unexpected end of string"),
}
}
}
impl Error for ParseDecimalError {}
impl From<TooLargeForDecimalError> for ParseDecimalError { impl From<TooLargeForDecimalError> for ParseDecimalError {
fn from(_: TooLargeForDecimalError) -> Self { fn from(_: TooLargeForDecimalError) -> Self {
Self { Self::Overflow
kind: DecimalParseErrorKind::Overflow,
}
} }
} }

@ -1,6 +1,6 @@
//! Implementation of [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/) //! Implementation of [SPARQL 1.1 Query Results CSV and TSV Formats](https://www.w3.org/TR/sparql11-results-csv-tsv/)
use crate::error::{ParseError, SyntaxError, SyntaxErrorKind, TextPosition}; use crate::error::{ParseError, SyntaxError, TextPosition};
use memchr::memchr; use memchr::memchr;
use oxrdf::vocab::xsd; use oxrdf::vocab::xsd;
use oxrdf::*; use oxrdf::*;
@ -508,23 +508,21 @@ impl<R: Read> TsvSolutionsReader<R> {
.sum::<usize>(); .sum::<usize>();
let start_position_bytes = let start_position_bytes =
line.split('\t').take(i).map(|c| c.len() + 1).sum::<usize>(); line.split('\t').take(i).map(|c| c.len() + 1).sum::<usize>();
SyntaxError { SyntaxError::Term {
inner: SyntaxErrorKind::Term { error: e,
error: e, term: v.into(),
term: v.into(), location: TextPosition {
location: TextPosition { line: self.reader.line_count - 1,
line: self.reader.line_count - 1, column: start_position_char.try_into().unwrap(),
column: start_position_char.try_into().unwrap(), offset: self.reader.last_line_start
offset: self.reader.last_line_start + u64::try_from(start_position_bytes).unwrap(),
+ u64::try_from(start_position_bytes).unwrap(), }..TextPosition {
}..TextPosition { line: self.reader.line_count - 1,
line: self.reader.line_count - 1, column: (start_position_char + v.chars().count())
column: (start_position_char + v.chars().count()) .try_into()
.try_into() .unwrap(),
.unwrap(), offset: self.reader.last_line_start
offset: self.reader.last_line_start + u64::try_from(start_position_bytes + v.len()).unwrap(),
+ u64::try_from(start_position_bytes + v.len()).unwrap(),
},
}, },
} }
})?)) })?))

@ -1,8 +1,7 @@
use oxrdf::TermParseError; use oxrdf::TermParseError;
use std::error::Error; use std::io;
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
use std::{fmt, io};
use thiserror::Error; use thiserror::Error;
/// Error returned during SPARQL result formats format parsing. /// Error returned during SPARQL result formats format parsing.
@ -42,28 +41,26 @@ impl From<quick_xml::Error> for ParseError {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e))) Self::Io(Arc::try_unwrap(error).unwrap_or_else(|e| io::Error::new(e.kind(), e)))
} }
_ => Self::Syntax(SyntaxError { _ => Self::Syntax(SyntaxError::Xml(error)),
inner: SyntaxErrorKind::Xml(error),
}),
} }
} }
} }
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct SyntaxError { pub enum SyntaxError {
pub(crate) inner: SyntaxErrorKind, #[error(transparent)]
} Json(#[from] json_event_parser::SyntaxError),
#[error(transparent)]
#[derive(Debug)] Xml(#[from] quick_xml::Error),
pub(crate) enum SyntaxErrorKind { #[error("Error {error} on '{term}' in line {}", location.start.line + 1)]
Json(json_event_parser::SyntaxError),
Xml(quick_xml::Error),
Term { Term {
#[source]
error: TermParseError, error: TermParseError,
term: String, term: String,
location: Range<TextPosition>, location: Range<TextPosition>,
}, },
#[error("{msg}")]
Msg { Msg {
msg: String, msg: String,
location: Option<Range<TextPosition>>, location: Option<Range<TextPosition>>,
@ -74,30 +71,26 @@ impl SyntaxError {
/// Builds an error from a printable error message. /// Builds an error from a printable error message.
#[inline] #[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self { Self::Msg {
inner: SyntaxErrorKind::Msg { msg: msg.into(),
msg: msg.into(), location: None,
location: None,
},
} }
} }
/// Builds an error from a printable error message and a location /// Builds an error from a printable error message and a location
#[inline] #[inline]
pub(crate) fn located_message(msg: impl Into<String>, location: Range<TextPosition>) -> Self { pub(crate) fn located_message(msg: impl Into<String>, location: Range<TextPosition>) -> Self {
Self { Self::Msg {
inner: SyntaxErrorKind::Msg { msg: msg.into(),
msg: msg.into(), location: Some(location),
location: Some(location),
},
} }
} }
/// The location of the error inside of the file. /// The location of the error inside of the file.
#[inline] #[inline]
pub fn location(&self) -> Option<Range<TextPosition>> { pub fn location(&self) -> Option<Range<TextPosition>> {
match &self.inner { match self {
SyntaxErrorKind::Json(e) => { Self::Json(e) => {
let location = e.location(); let location = e.location();
Some( Some(
TextPosition { TextPosition {
@ -111,37 +104,9 @@ impl SyntaxError {
}, },
) )
} }
SyntaxErrorKind::Term { location, .. } => Some(location.clone()), Self::Term { location, .. } => Some(location.clone()),
SyntaxErrorKind::Msg { location, .. } => location.clone(), Self::Msg { location, .. } => location.clone(),
SyntaxErrorKind::Xml(_) => None, Self::Xml(_) => None,
}
}
}
impl fmt::Display for SyntaxError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
SyntaxErrorKind::Json(e) => e.fmt(f),
SyntaxErrorKind::Xml(e) => e.fmt(f),
SyntaxErrorKind::Term {
error,
term,
location,
} => write!(f, "{error} on '{term}' in line {}", location.start.line + 1),
SyntaxErrorKind::Msg { msg, .. } => f.write_str(msg),
}
}
}
impl Error for SyntaxError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.inner {
SyntaxErrorKind::Json(e) => Some(e),
SyntaxErrorKind::Xml(e) => Some(e),
SyntaxErrorKind::Term { error, .. } => Some(error),
SyntaxErrorKind::Msg { .. } => None,
} }
} }
} }
@ -149,9 +114,9 @@ impl Error for SyntaxError {
impl From<SyntaxError> for io::Error { impl From<SyntaxError> for io::Error {
#[inline] #[inline]
fn from(error: SyntaxError) -> Self { fn from(error: SyntaxError) -> Self {
match error.inner { match error {
SyntaxErrorKind::Json(error) => Self::new(io::ErrorKind::InvalidData, error), SyntaxError::Json(error) => Self::new(io::ErrorKind::InvalidData, error),
SyntaxErrorKind::Xml(error) => match error { SyntaxError::Xml(error) => match error {
quick_xml::Error::Io(error) => { quick_xml::Error::Io(error) => {
Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e)) Arc::try_unwrap(error).unwrap_or_else(|e| Self::new(e.kind(), e))
} }
@ -160,16 +125,8 @@ impl From<SyntaxError> for io::Error {
} }
_ => Self::new(io::ErrorKind::InvalidData, error), _ => Self::new(io::ErrorKind::InvalidData, error),
}, },
SyntaxErrorKind::Term { .. } => Self::new(io::ErrorKind::InvalidData, error), SyntaxError::Term { .. } => Self::new(io::ErrorKind::InvalidData, error),
SyntaxErrorKind::Msg { msg, .. } => Self::new(io::ErrorKind::InvalidData, msg), SyntaxError::Msg { msg, .. } => Self::new(io::ErrorKind::InvalidData, msg),
}
}
}
impl From<json_event_parser::SyntaxError> for SyntaxError {
fn from(error: json_event_parser::SyntaxError) -> Self {
Self {
inner: SyntaxErrorKind::Json(error),
} }
} }
} }

@ -25,6 +25,7 @@ oxiri.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
peg.workspace = true peg.workspace = true
rand.workspace = true rand.workspace = true
thiserror.workspace = true
[lints] [lints]
workspace = true workspace = true

@ -9,26 +9,22 @@ use oxrdf::vocab::{rdf, xsd};
use peg::parser; use peg::parser;
use peg::str::LineCol; use peg::str::LineCol;
use rand::random; use rand::random;
use std::char;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::error::Error;
use std::mem::take; use std::mem::take;
use std::str::FromStr; use std::str::FromStr;
use std::{char, fmt}; use thiserror::Error;
/// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query. /// Parses a SPARQL query with an optional base IRI to resolve relative IRIs in the query.
pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result<Query, ParseError> { pub fn parse_query(query: &str, base_iri: Option<&str>) -> Result<Query, ParseError> {
let mut state = ParserState::from_base_iri(base_iri)?; let mut state = ParserState::from_base_iri(base_iri)?;
parser::QueryUnit(query, &mut state).map_err(|e| ParseError { parser::QueryUnit(query, &mut state).map_err(|e| ParseError::Parser(e))
inner: ParseErrorKind::Parser(e),
})
} }
/// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query. /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, ParseError> { pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, ParseError> {
let mut state = ParserState::from_base_iri(base_iri)?; let mut state = ParserState::from_base_iri(base_iri)?;
let operations = parser::UpdateInit(update, &mut state).map_err(|e| ParseError { let operations = parser::UpdateInit(update, &mut state).map_err(|e| ParseError::Parser(e))?;
inner: ParseErrorKind::Parser(e),
})?;
Ok(Update { Ok(Update {
operations, operations,
base_iri: state.base_iri, base_iri: state.base_iri,
@ -36,37 +32,12 @@ pub fn parse_update(update: &str, base_iri: Option<&str>) -> Result<Update, Pars
} }
/// Error returned during SPARQL parsing. /// Error returned during SPARQL parsing.
#[derive(Debug)] #[derive(Debug, Error)]
pub struct ParseError { pub enum ParseError {
inner: ParseErrorKind, #[error("Invalid SPARQL base IRI provided: {0}")]
} InvalidBaseIri(#[from] IriParseError),
#[error(transparent)]
#[derive(Debug)] Parser(#[from] peg::error::ParseError<LineCol>),
enum ParseErrorKind {
InvalidBaseIri(IriParseError),
Parser(peg::error::ParseError<LineCol>),
}
impl fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.inner {
ParseErrorKind::InvalidBaseIri(e) => {
write!(f, "Invalid SPARQL base IRI provided: {e}")
}
ParseErrorKind::Parser(e) => e.fmt(f),
}
}
}
impl Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match &self.inner {
ParseErrorKind::InvalidBaseIri(e) => Some(e),
ParseErrorKind::Parser(e) => Some(e),
}
}
} }
struct AnnotatedTerm { struct AnnotatedTerm {
@ -697,9 +668,7 @@ impl ParserState {
pub(crate) fn from_base_iri(base_iri: Option<&str>) -> Result<Self, ParseError> { pub(crate) fn from_base_iri(base_iri: Option<&str>) -> Result<Self, ParseError> {
Ok(Self { Ok(Self {
base_iri: if let Some(base_iri) = base_iri { base_iri: if let Some(base_iri) = base_iri {
Some(Iri::parse(base_iri.to_owned()).map_err(|e| ParseError { Some(Iri::parse(base_iri.to_owned()).map_err(|e| ParseError::InvalidBaseIri(e))?)
inner: ParseErrorKind::InvalidBaseIri(e),
})?)
} else { } else {
None None
}, },

Loading…
Cancel
Save