pull/745/merge
Yuri Astrakhan 1 year ago committed by GitHub
commit 31c4e9f7f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 70
      Cargo.lock
  2. 2
      Cargo.toml
  3. 1
      lib/oxigraph/Cargo.toml
  4. 110
      lib/oxigraph/src/sparql/error.rs
  5. 8
      lib/oxigraph/src/sparql/eval.rs
  6. 10
      lib/oxigraph/src/storage/backend/rocksdb.rs
  7. 11
      lib/oxigraph/src/storage/binary_encoder.rs
  8. 191
      lib/oxigraph/src/storage/error.rs
  9. 33
      lib/oxigraph/src/storage/numeric_encoder.rs
  10. 30
      lib/oxigraph/src/storage/small_string.rs
  11. 1
      lib/oxrdf/Cargo.toml
  12. 13
      lib/oxrdf/src/blank_node.rs
  13. 10
      lib/oxrdf/src/literal.rs
  14. 69
      lib/oxrdf/src/parser.rs
  15. 13
      lib/oxrdf/src/variable.rs
  16. 1
      lib/oxrdfio/Cargo.toml
  17. 119
      lib/oxrdfio/src/error.rs
  18. 1
      lib/oxrdfxml/Cargo.toml
  19. 108
      lib/oxrdfxml/src/error.rs
  20. 18
      lib/oxrdfxml/src/parser.rs
  21. 4
      lib/oxsdatatypes/Cargo.toml
  22. 84
      lib/oxsdatatypes/src/date_time.rs
  23. 77
      lib/oxsdatatypes/src/decimal.rs
  24. 35
      lib/oxsdatatypes/src/duration.rs
  25. 77
      lib/oxsdatatypes/src/geopoint.rs
  26. 12
      lib/oxsdatatypes/src/integer.rs
  27. 2
      lib/oxsdatatypes/src/lib.rs
  28. 1
      lib/oxttl/Cargo.toml
  29. 47
      lib/oxttl/src/toolkit/error.rs
  30. 1
      lib/sparesults/Cargo.toml
  31. 6
      lib/sparesults/src/csv.rs
  32. 131
      lib/sparesults/src/error.rs
  33. 1
      lib/spargebra/Cargo.toml
  34. 52
      lib/spargebra/src/parser.rs

70
Cargo.lock generated

@ -290,9 +290,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.18" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -300,9 +300,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.4.18" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@ -312,9 +312,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.4.7" version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -324,9 +324,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.6.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]] [[package]]
name = "codspeed" name = "codspeed"
@ -751,12 +751,12 @@ checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.10" version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" checksum = "fe8f25ce1159c7740ff0b9b2f5cdf4a8428742ba7c112b9f20f22cd5219c7dab"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi",
"rustix", "libc",
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
@ -786,9 +786,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]] [[package]]
name = "jobserver" name = "jobserver"
version = "0.1.27" version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -1068,6 +1068,7 @@ dependencies = [
"sparesults", "sparesults",
"spargebra", "spargebra",
"sparopt", "sparopt",
"thiserror",
"zstd", "zstd",
] ]
@ -1135,6 +1136,7 @@ dependencies = [
"oxiri", "oxiri",
"oxsdatatypes", "oxsdatatypes",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]
@ -1144,6 +1146,7 @@ dependencies = [
"oxrdf", "oxrdf",
"oxrdfxml", "oxrdfxml",
"oxttl", "oxttl",
"thiserror",
"tokio", "tokio",
] ]
@ -1155,6 +1158,7 @@ dependencies = [
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"quick-xml", "quick-xml",
"thiserror",
"tokio", "tokio",
] ]
@ -1173,6 +1177,8 @@ name = "oxsdatatypes"
version = "0.2.0-alpha.1" version = "0.2.0-alpha.1"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"thiserror",
"wkt",
] ]
[[package]] [[package]]
@ -1183,6 +1189,7 @@ dependencies = [
"oxilangtag", "oxilangtag",
"oxiri", "oxiri",
"oxrdf", "oxrdf",
"thiserror",
"tokio", "tokio",
] ]
@ -1744,6 +1751,7 @@ dependencies = [
"memchr", "memchr",
"oxrdf", "oxrdf",
"quick-xml", "quick-xml",
"thiserror",
"tokio", "tokio",
] ]
@ -1756,6 +1764,7 @@ dependencies = [
"oxrdf", "oxrdf",
"peg", "peg",
"rand", "rand",
"thiserror",
] ]
[[package]] [[package]]
@ -1782,9 +1791,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]] [[package]]
name = "subtle" name = "subtle"
@ -1847,6 +1856,26 @@ dependencies = [
"term", "term",
] ]
[[package]]
name = "thiserror"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.34" version = "0.3.34"
@ -2285,6 +2314,17 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "wkt"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c2252781f8927974e8ba6a67c965a759a2b88ea2b1825f6862426bbb1c8f41"
dependencies = [
"log",
"num-traits",
"thiserror",
]
[[package]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.7.0" version = "1.7.0"

@ -60,10 +60,12 @@ sha1 = "0.10"
sha2 = "0.10" sha2 = "0.10"
siphasher = ">=0.3, <2.0" siphasher = ">=0.3, <2.0"
text-diff = "0.4" text-diff = "0.4"
thiserror = "1.0.50"
time = "0.3" time = "0.3"
tokio = "1.29" tokio = "1.29"
url = "2.4" url = "2.4"
wasm-bindgen = "0.2.83" wasm-bindgen = "0.2.83"
wkt = { version = "0.10.3", default-features = false }
zstd = ">=0.12, <0.14" zstd = ">=0.12, <0.14"
# Internal dependencies # Internal dependencies

@ -43,6 +43,7 @@ siphasher.workspace = true
sparesults = { workspace = true, features = ["rdf-star"] } sparesults = { workspace = true, features = ["rdf-star"] }
spargebra = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] } spargebra = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] }
sparopt = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] } sparopt = { workspace = true, features = ["rdf-star", "sep-0002", "sep-0006"] }
thiserror.workspace = true
[target.'cfg(not(target_family = "wasm"))'.dependencies] [target.'cfg(not(target_family = "wasm"))'.dependencies]
libc.workspace = true libc.workspace = true

@ -5,93 +5,53 @@ use crate::sparql::ParseError;
use crate::storage::StorageError; use crate::storage::StorageError;
use std::convert::Infallible; use std::convert::Infallible;
use std::error::Error; use std::error::Error;
use std::{fmt, io}; use std::io;
/// A SPARQL evaluation error. /// A SPARQL evaluation error.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum EvaluationError { pub enum EvaluationError {
/// An error in SPARQL parsing. /// An error in SPARQL parsing.
Parsing(ParseError), #[error(transparent)]
Parsing(#[from] ParseError),
/// An error from the storage. /// An error from the storage.
Storage(StorageError), #[error(transparent)]
Storage(#[from] StorageError),
/// An error while parsing an external RDF file. /// An error while parsing an external RDF file.
GraphParsing(RdfParseError), #[error(transparent)]
GraphParsing(#[from] RdfParseError),
/// An error while parsing an external result file (likely from a federated query). /// An error while parsing an external result file (likely from a federated query).
ResultsParsing(ResultsParseError), #[error(transparent)]
ResultsParsing(#[from] ResultsParseError),
/// An error returned during results serialization. /// An error returned during results serialization.
ResultsSerialization(io::Error), #[error(transparent)]
ResultsSerialization(#[from] io::Error),
/// Error during `SERVICE` evaluation /// Error during `SERVICE` evaluation
Service(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
Service(#[source] Box<dyn Error + Send + Sync + 'static>),
/// Error when `CREATE` tries to create an already existing graph /// Error when `CREATE` tries to create an already existing graph
#[error("The graph {0} already exists")]
GraphAlreadyExists(NamedNode), GraphAlreadyExists(NamedNode),
/// Error when `DROP` or `CLEAR` tries to remove a not existing graph /// Error when `DROP` or `CLEAR` tries to remove a not existing graph
#[error("The graph {0} does not exist")]
GraphDoesNotExist(NamedNode), GraphDoesNotExist(NamedNode),
/// The variable storing the `SERVICE` name is unbound /// The variable storing the `SERVICE` name is unbound
#[error("The variable encoding the service name is unbound")]
UnboundService, UnboundService,
/// The given `SERVICE` is not supported /// The given `SERVICE` is not supported
#[error("The service {0} is not supported")]
UnsupportedService(NamedNode), UnsupportedService(NamedNode),
/// The given content media type returned from an HTTP response is not supported (`SERVICE` and `LOAD`) /// The given content media type returned from an HTTP response is not supported (`SERVICE` and `LOAD`)
#[error("The content media type {0} is not supported")]
UnsupportedContentType(String), UnsupportedContentType(String),
/// The `SERVICE` call has not returns solutions /// The `SERVICE` call has not returns solutions
#[error("The service is not returning solutions but a boolean or a graph")]
ServiceDoesNotReturnSolutions, ServiceDoesNotReturnSolutions,
/// The results are not a RDF graph /// The results are not a RDF graph
#[error("The query results are not a RDF graph")]
NotAGraph, NotAGraph,
} }
impl fmt::Display for EvaluationError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parsing(error) => error.fmt(f),
Self::Storage(error) => error.fmt(f),
Self::GraphParsing(error) => error.fmt(f),
Self::ResultsParsing(error) => error.fmt(f),
Self::ResultsSerialization(error) => error.fmt(f),
Self::Service(error) => error.fmt(f),
Self::GraphAlreadyExists(graph) => write!(f, "The graph {graph} already exists"),
Self::GraphDoesNotExist(graph) => write!(f, "The graph {graph} does not exist"),
Self::UnboundService => {
f.write_str("The variable encoding the service name is unbound")
}
Self::UnsupportedService(service) => {
write!(f, "The service {service} is not supported")
}
Self::UnsupportedContentType(content_type) => {
write!(f, "The content media type {content_type} is not supported")
}
Self::ServiceDoesNotReturnSolutions => {
f.write_str("The service is not returning solutions but a boolean or a graph")
}
Self::NotAGraph => f.write_str("The query results are not a RDF graph"),
}
}
}
impl Error for EvaluationError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Parsing(e) => Some(e),
Self::Storage(e) => Some(e),
Self::GraphParsing(e) => Some(e),
Self::ResultsParsing(e) => Some(e),
Self::ResultsSerialization(e) => Some(e),
Self::Service(e) => {
let e = Box::as_ref(e);
Some(e)
}
Self::GraphAlreadyExists(_)
| Self::GraphDoesNotExist(_)
| Self::UnboundService
| Self::UnsupportedService(_)
| Self::UnsupportedContentType(_)
| Self::ServiceDoesNotReturnSolutions
| Self::NotAGraph => None,
}
}
}
impl From<Infallible> for EvaluationError { impl From<Infallible> for EvaluationError {
#[inline] #[inline]
fn from(error: Infallible) -> Self { fn from(error: Infallible) -> Self {
@ -99,34 +59,6 @@ impl From<Infallible> for EvaluationError {
} }
} }
impl From<ParseError> for EvaluationError {
#[inline]
fn from(error: ParseError) -> Self {
Self::Parsing(error)
}
}
impl From<StorageError> for EvaluationError {
#[inline]
fn from(error: StorageError) -> Self {
Self::Storage(error)
}
}
impl From<RdfParseError> for EvaluationError {
#[inline]
fn from(error: RdfParseError) -> Self {
Self::GraphParsing(error)
}
}
impl From<ResultsParseError> for EvaluationError {
#[inline]
fn from(error: ResultsParseError) -> Self {
Self::ResultsParsing(error)
}
}
impl From<EvaluationError> for io::Error { impl From<EvaluationError> for io::Error {
#[inline] #[inline]
fn from(error: EvaluationError) -> Self { fn from(error: EvaluationError) -> Self {

@ -13,6 +13,7 @@ use json_event_parser::{JsonEvent, ToWriteJsonWriter};
use md5::Md5; use md5::Md5;
use oxilangtag::LanguageTag; use oxilangtag::LanguageTag;
use oxiri::Iri; use oxiri::Iri;
use oxrdf::vocab::geosparql;
use oxrdf::{TermRef, Variable}; use oxrdf::{TermRef, Variable};
use oxsdatatypes::*; use oxsdatatypes::*;
use rand::random; use rand::random;
@ -2930,6 +2931,7 @@ fn to_string_id(dataset: &DatasetView, term: &EncodedTerm) -> Option<SmallString
EncodedTerm::DayTimeDurationLiteral(value) => { EncodedTerm::DayTimeDurationLiteral(value) => {
Some(build_string_id(dataset, &value.to_string())) Some(build_string_id(dataset, &value.to_string()))
} }
EncodedTerm::GeoPoint(value) => Some(build_string_id(dataset, &value.to_string())),
} }
} }
@ -3318,6 +3320,11 @@ fn equals(a: &EncodedTerm, b: &EncodedTerm) -> Option<bool> {
_ if b.is_unknown_typed_literal() => None, _ if b.is_unknown_typed_literal() => None,
_ => Some(false), _ => Some(false),
}, },
EncodedTerm::GeoPoint(a) => match b {
EncodedTerm::GeoPoint(b) => Some(a == b),
_ if b.is_unknown_typed_literal() => None,
_ => Some(false),
},
EncodedTerm::Triple(a) => { EncodedTerm::Triple(a) => {
if let EncodedTerm::Triple(b) = b { if let EncodedTerm::Triple(b) = b {
Some( Some(
@ -3672,6 +3679,7 @@ fn datatype(dataset: &DatasetView, value: &EncodedTerm) -> Option<EncodedTerm> {
EncodedTerm::DayTimeDurationLiteral(..) => { EncodedTerm::DayTimeDurationLiteral(..) => {
Some(encode_named_node(dataset, xsd::DAY_TIME_DURATION)) Some(encode_named_node(dataset, xsd::DAY_TIME_DURATION))
} }
EncodedTerm::GeoPoint(..) => Some(encode_named_node(dataset, geosparql::WKT_LITERAL)),
} }
} }

@ -1314,6 +1314,8 @@ impl SstFileWriter {
} }
} }
#[derive(thiserror::Error)]
#[error("{}", self.message())]
struct ErrorStatus(rocksdb_status_t); struct ErrorStatus(rocksdb_status_t);
unsafe impl Send for ErrorStatus {} unsafe impl Send for ErrorStatus {}
@ -1352,14 +1354,6 @@ impl fmt::Debug for ErrorStatus {
} }
} }
impl fmt::Display for ErrorStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.message())
}
}
impl Error for ErrorStatus {}
impl From<ErrorStatus> for StorageError { impl From<ErrorStatus> for StorageError {
fn from(status: ErrorStatus) -> Self { fn from(status: ErrorStatus) -> Self {
if status.0.code == rocksdb_status_code_t_rocksdb_status_code_io_error { if status.0.code == rocksdb_status_code_t_rocksdb_status_code_io_error {

@ -46,6 +46,7 @@ const TYPE_G_MONTH_LITERAL: u8 = 41;
const TYPE_DURATION_LITERAL: u8 = 42; const TYPE_DURATION_LITERAL: u8 = 42;
const TYPE_YEAR_MONTH_DURATION_LITERAL: u8 = 43; const TYPE_YEAR_MONTH_DURATION_LITERAL: u8 = 43;
const TYPE_DAY_TIME_DURATION_LITERAL: u8 = 44; const TYPE_DAY_TIME_DURATION_LITERAL: u8 = 44;
const TYPE_GEO_POINT_LITERAL: u8 = 45;
const TYPE_TRIPLE: u8 = 48; const TYPE_TRIPLE: u8 = 48;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -388,6 +389,11 @@ impl<R: Read> TermReader for R {
self.read_exact(&mut buffer)?; self.read_exact(&mut buffer)?;
Ok(DayTimeDuration::from_be_bytes(buffer).into()) Ok(DayTimeDuration::from_be_bytes(buffer).into())
} }
TYPE_GEO_POINT_LITERAL => {
let mut buffer = [0; 16];
self.read_exact(&mut buffer)?;
Ok(DayTimeDuration::from_be_bytes(buffer).into())
}
TYPE_TRIPLE => Ok(EncodedTriple { TYPE_TRIPLE => Ok(EncodedTriple {
subject: self.read_term()?, subject: self.read_term()?,
predicate: self.read_term()?, predicate: self.read_term()?,
@ -622,6 +628,11 @@ pub fn write_term(sink: &mut Vec<u8>, term: &EncodedTerm) {
sink.push(TYPE_DAY_TIME_DURATION_LITERAL); sink.push(TYPE_DAY_TIME_DURATION_LITERAL);
sink.extend_from_slice(&value.to_be_bytes()) sink.extend_from_slice(&value.to_be_bytes())
} }
EncodedTerm::GeoPoint(value) => {
sink.push(TYPE_GEO_POINT_LITERAL);
sink.extend_from_slice(&value.x.to_be_bytes());
sink.extend_from_slice(&value.y.to_be_bytes());
}
EncodedTerm::Triple(value) => { EncodedTerm::Triple(value) => {
sink.push(TYPE_TRIPLE); sink.push(TYPE_TRIPLE);
write_term(sink, &value.subject); write_term(sink, &value.subject);

@ -1,47 +1,23 @@
use crate::io::{ParseError, RdfFormat}; use crate::io::{ParseError, RdfFormat};
use crate::storage::numeric_encoder::EncodedTerm;
use oxiri::IriParseError; use oxiri::IriParseError;
use oxrdf::TermRef;
use std::error::Error; use std::error::Error;
use std::{fmt, io}; use std::io;
/// An error related to storage operations (reads, writes...). /// An error related to storage operations (reads, writes...).
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[non_exhaustive] #[non_exhaustive]
pub enum StorageError { pub enum StorageError {
/// Error from the OS I/O layer. /// Error from the OS I/O layer.
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// Error related to data corruption. /// Error related to data corruption.
Corruption(CorruptionError), #[error(transparent)]
Corruption(#[from] CorruptionError),
#[doc(hidden)] #[doc(hidden)]
Other(Box<dyn Error + Send + Sync + 'static>), #[error("{0}")]
} Other(#[source] Box<dyn Error + Send + Sync + 'static>),
impl fmt::Display for StorageError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Corruption(e) => e.fmt(f),
Self::Other(e) => e.fmt(f),
}
}
}
impl Error for StorageError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Corruption(e) => Some(e),
Self::Other(e) => Some(e.as_ref()),
}
}
}
impl From<io::Error> for StorageError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
} }
impl From<StorageError> for io::Error { impl From<StorageError> for io::Error {
@ -56,59 +32,32 @@ 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, thiserror::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.
#[inline]
pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self {
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 { /// Builds an error from encoding and the term.
#[inline] #[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> { pub(crate) fn from_encoded_term(encoded: &EncodedTerm, term: &TermRef<'_>) -> Self {
match &self.inner { // TODO: eventually use a dedicated error enum value
CorruptionErrorKind::Msg(_) => None, Self::new(format!("Invalid term encoding {encoded:?} for {term}"))
CorruptionErrorKind::Other(e) => Some(e.as_ref()),
}
}
} }
impl From<CorruptionError> for StorageError { /// Builds an error from a printable error message.
#[inline] #[inline]
fn from(error: CorruptionError) -> Self { pub(crate) fn msg(msg: impl Into<String>) -> Self {
Self::Corruption(error) Self::Msg(msg.into())
} }
} }
@ -120,57 +69,25 @@ impl From<CorruptionError> for io::Error {
} }
/// An error raised while loading a file into a [`Store`](crate::store::Store). /// An error raised while loading a file into a [`Store`](crate::store::Store).
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum LoaderError { pub enum LoaderError {
/// An error raised while reading the file. /// An error raised while reading the file.
Parsing(ParseError), #[error(transparent)]
Parsing(#[from] ParseError),
/// An error raised during the insertion in the store. /// An error raised during the insertion in the store.
Storage(StorageError), #[error(transparent)]
Storage(#[from] StorageError),
/// The base IRI is invalid. /// The base IRI is invalid.
#[error("Invalid base IRI '{iri}': {error}")]
InvalidBaseIri { InvalidBaseIri {
/// The IRI itself. /// The IRI itself.
iri: String, iri: String,
/// The parsing error. /// The parsing error.
#[source]
error: IriParseError, error: IriParseError,
}, },
} }
impl fmt::Display for LoaderError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parsing(e) => e.fmt(f),
Self::Storage(e) => e.fmt(f),
Self::InvalidBaseIri { iri, error } => write!(f, "Invalid base IRI '{iri}': {error}"),
}
}
}
impl Error for LoaderError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Parsing(e) => Some(e),
Self::Storage(e) => Some(e),
Self::InvalidBaseIri { error, .. } => Some(error),
}
}
}
impl From<ParseError> for LoaderError {
#[inline]
fn from(error: ParseError) -> Self {
Self::Parsing(error)
}
}
impl From<StorageError> for LoaderError {
#[inline]
fn from(error: StorageError) -> Self {
Self::Storage(error)
}
}
impl From<LoaderError> for io::Error { impl From<LoaderError> for io::Error {
#[inline] #[inline]
fn from(error: LoaderError) -> Self { fn from(error: LoaderError) -> Self {
@ -185,55 +102,19 @@ impl From<LoaderError> for io::Error {
} }
/// An error raised while writing a file from a [`Store`](crate::store::Store). /// An error raised while writing a file from a [`Store`](crate::store::Store).
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum SerializerError { pub enum SerializerError {
/// An error raised while writing the content. /// An error raised while writing the content.
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error raised during the lookup in the store. /// An error raised during the lookup in the store.
Storage(StorageError), #[error(transparent)]
Storage(#[from] StorageError),
/// A format compatible with [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) is required. /// A format compatible with [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) is required.
#[error("A RDF format supporting datasets was expected, {0} found")]
DatasetFormatExpected(RdfFormat), DatasetFormatExpected(RdfFormat),
} }
impl fmt::Display for SerializerError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Storage(e) => e.fmt(f),
Self::DatasetFormatExpected(format) => write!(
f,
"A RDF format supporting datasets was expected, {format} found"
),
}
}
}
impl Error for SerializerError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Storage(e) => Some(e),
Self::DatasetFormatExpected(_) => None,
}
}
}
impl From<io::Error> for SerializerError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<StorageError> for SerializerError {
#[inline]
fn from(error: StorageError) -> Self {
Self::Storage(error)
}
}
impl From<SerializerError> for io::Error { impl From<SerializerError> for io::Error {
#[inline] #[inline]
fn from(error: SerializerError) -> Self { fn from(error: SerializerError) -> Self {

@ -95,6 +95,7 @@ pub enum EncodedTerm {
DurationLiteral(Duration), DurationLiteral(Duration),
YearMonthDurationLiteral(YearMonthDuration), YearMonthDurationLiteral(YearMonthDuration),
DayTimeDurationLiteral(DayTimeDuration), DayTimeDurationLiteral(DayTimeDuration),
GeoPoint(GeoPoint),
Triple(Arc<EncodedTriple>), Triple(Arc<EncodedTriple>),
} }
@ -266,6 +267,7 @@ impl Hash for EncodedTerm {
Self::YearMonthDurationLiteral(value) => value.hash(state), Self::YearMonthDurationLiteral(value) => value.hash(state),
Self::DayTimeDurationLiteral(value) => value.hash(state), Self::DayTimeDurationLiteral(value) => value.hash(state),
Self::Triple(value) => value.hash(state), Self::Triple(value) => value.hash(state),
EncodedTerm::GeoPoint(_) => {}
} }
} }
} }
@ -713,19 +715,13 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let EncodedTerm::NamedNode { iri_id } = encoded { if let EncodedTerm::NamedNode { iri_id } = encoded {
insert_str(iri_id, node.as_str()) insert_str(iri_id, node.as_str())
} else { } else {
Err( Err(CorruptionError::from_encoded_term(encoded, &term).into())
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
)
} }
} }
TermRef::BlankNode(node) => match encoded { TermRef::BlankNode(node) => match encoded {
EncodedTerm::BigBlankNode { id_id } => insert_str(id_id, node.as_str()), EncodedTerm::BigBlankNode { id_id } => insert_str(id_id, node.as_str()),
EncodedTerm::SmallBlankNode(..) | EncodedTerm::NumericalBlankNode { .. } => Ok(()), EncodedTerm::SmallBlankNode(..) | EncodedTerm::NumericalBlankNode { .. } => Ok(()),
_ => Err( _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()),
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
),
}, },
TermRef::Literal(literal) => match encoded { TermRef::Literal(literal) => match encoded {
EncodedTerm::BigStringLiteral { value_id } EncodedTerm::BigStringLiteral { value_id }
@ -736,10 +732,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
insert_str(language_id, language) insert_str(language_id, language)
} else { } else {
Err(CorruptionError::new(format!( Err(CorruptionError::from_encoded_term(encoded, &term).into())
"Invalid term encoding {encoded:?} for {term}"
))
.into())
} }
} }
EncodedTerm::BigBigLangStringLiteral { EncodedTerm::BigBigLangStringLiteral {
@ -750,10 +743,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
if let Some(language) = literal.language() { if let Some(language) = literal.language() {
insert_str(language_id, language) insert_str(language_id, language)
} else { } else {
Err(CorruptionError::new(format!( Err(CorruptionError::from_encoded_term(encoded, &term).into())
"Invalid term encoding {encoded:?} for {term}"
))
.into())
} }
} }
EncodedTerm::SmallTypedLiteral { datatype_id, .. } => { EncodedTerm::SmallTypedLiteral { datatype_id, .. } => {
@ -784,10 +774,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
| EncodedTerm::DurationLiteral(..) | EncodedTerm::DurationLiteral(..)
| EncodedTerm::YearMonthDurationLiteral(..) | EncodedTerm::YearMonthDurationLiteral(..)
| EncodedTerm::DayTimeDurationLiteral(..) => Ok(()), | EncodedTerm::DayTimeDurationLiteral(..) => Ok(()),
_ => Err( _ => Err(CorruptionError::from_encoded_term(encoded, &term).into()),
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
),
}, },
TermRef::Triple(triple) => { TermRef::Triple(triple) => {
if let EncodedTerm::Triple(encoded) = encoded { if let EncodedTerm::Triple(encoded) = encoded {
@ -799,10 +786,7 @@ pub fn insert_term<F: FnMut(&StrHash, &str) -> Result<(), StorageError>>(
)?; )?;
insert_term(triple.object.as_ref(), &encoded.object, insert_str) insert_term(triple.object.as_ref(), &encoded.object, insert_str)
} else { } else {
Err( Err(CorruptionError::from_encoded_term(encoded, &term).into())
CorruptionError::new(format!("Invalid term encoding {encoded:?} for {term}"))
.into(),
)
} }
} }
} }
@ -1035,6 +1019,7 @@ impl<S: StrLookup> Decoder for S {
EncodedTerm::DurationLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::DurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::YearMonthDurationLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::YearMonthDurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::DayTimeDurationLiteral(value) => Ok(Literal::from(*value).into()), EncodedTerm::DayTimeDurationLiteral(value) => Ok(Literal::from(*value).into()),
EncodedTerm::GeoPoint(value) => Ok(Literal::from(*value).into()),
EncodedTerm::Triple(triple) => Ok(self.decode_triple(triple)?.into()), EncodedTerm::Triple(triple) => Ok(self.decode_triple(triple)?.into()),
} }
} }

@ -1,6 +1,5 @@
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops::Deref; use std::ops::Deref;
use std::str::{FromStr, Utf8Error}; use std::str::{FromStr, Utf8Error};
@ -169,31 +168,10 @@ impl<'a> TryFrom<&'a str> for SmallString {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
pub enum BadSmallStringError { pub enum BadSmallStringError {
#[error("small strings could only contain at most 15 characters, found {0}")]
TooLong(usize), TooLong(usize),
BadUtf8(Utf8Error), #[error(transparent)]
} BadUtf8(#[from] Utf8Error),
impl fmt::Display for BadSmallStringError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooLong(v) => write!(
f,
"small strings could only contain at most 15 characters, found {v}"
),
Self::BadUtf8(e) => e.fmt(f),
}
}
}
impl Error for BadSmallStringError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::TooLong(_) => None,
Self::BadUtf8(e) => Some(e),
}
}
} }

@ -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

@ -1,5 +1,4 @@
use rand::random; use rand::random;
use std::error::Error;
use std::io::Write; use std::io::Write;
use std::{fmt, str}; use std::{fmt, str};
@ -345,18 +344,10 @@ fn to_integer_id(id: &str) -> Option<u128> {
} }
/// An error raised during [`BlankNode`] IDs validation. /// An error raised during [`BlankNode`] IDs validation.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[error("The blank node identifier is invalid")]
pub struct BlankNodeIdParseError; pub struct BlankNodeIdParseError;
impl fmt::Display for BlankNodeIdParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The blank node identifier is invalid")
}
}
impl Error for BlankNodeIdParseError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -1,5 +1,5 @@
use crate::named_node::NamedNode; use crate::named_node::NamedNode;
use crate::vocab::{rdf, xsd}; use crate::vocab::{geosparql, rdf, xsd};
use crate::NamedNodeRef; use crate::NamedNodeRef;
use oxilangtag::{LanguageTag, LanguageTagParseError}; use oxilangtag::{LanguageTag, LanguageTagParseError};
#[cfg(feature = "oxsdatatypes")] #[cfg(feature = "oxsdatatypes")]
@ -422,6 +422,14 @@ impl From<DayTimeDuration> for Literal {
} }
} }
#[cfg(feature = "oxsdatatypes")]
impl From<GeoPoint> for Literal {
#[inline]
fn from(value: GeoPoint) -> Self {
Self::new_typed_literal(value.to_string(), geosparql::WKT_LITERAL)
}
}
/// A borrowed RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). /// A borrowed RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal).
/// ///
/// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation: /// The default string formatter is returning an N-Triples, Turtle, and SPARQL compatible representation:

@ -5,9 +5,8 @@ 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};
/// 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 +165,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 +180,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 +202,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,12 +230,10 @@ 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 +412,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, thiserror::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,5 +1,4 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::fmt; use std::fmt;
/// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) owned variable. /// A [SPARQL query](https://www.w3.org/TR/sparql11-query/) owned variable.
@ -212,14 +211,6 @@ fn validate_variable_identifier(id: &str) -> Result<(), VariableNameParseError>
} }
/// An error raised during [`Variable`] name validation. /// An error raised during [`Variable`] name validation.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
#[error("The variable name is invalid")]
pub struct VariableNameParseError; pub struct VariableNameParseError;
impl fmt::Display for VariableNameParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The variable name is invalid")
}
}
impl Error for VariableNameParseError {}

@ -22,6 +22,7 @@ rdf-star = ["oxrdf/rdf-star", "oxttl/rdf-star"]
oxrdf.workspace = true oxrdf.workspace = true
oxrdfxml.workspace = true oxrdfxml.workspace = true
oxttl.workspace = true oxttl.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,50 +1,20 @@
use std::error::Error; use std::io;
use std::ops::Range; use std::ops::Range;
use std::{fmt, io};
/// Error returned during RDF format parsing. /// Error returned during RDF format parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
Syntax(#[from] SyntaxError),
} }
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 fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Syntax(e) => e.fmt(f),
}
}
}
impl Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Syntax(e) => Some(e),
}
}
}
impl From<oxttl::SyntaxError> for SyntaxError {
#[inline]
fn from(error: oxttl::SyntaxError) -> Self {
Self {
inner: SyntaxErrorKind::Turtle(error),
}
} }
} }
@ -58,15 +28,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 {
@ -77,20 +38,6 @@ impl From<oxrdfxml::ParseError> for ParseError {
} }
} }
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
}
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
#[inline] #[inline]
fn from(error: ParseError) -> Self { fn from(error: ParseError) -> Self {
@ -102,15 +49,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, thiserror::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 },
} }
@ -118,8 +63,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 {
@ -133,29 +78,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,
} }
} }
} }
@ -163,10 +86,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),
} }
} }
} }

@ -22,6 +22,7 @@ oxilangtag.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
quick-xml.workspace = true quick-xml.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,50 +1,17 @@
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};
/// Error returned during RDF/XML parsing. /// Error returned during RDF/XML parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] SyntaxError),
impl fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Syntax(e) => e.fmt(f),
}
}
}
impl Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Syntax(e) => Some(e),
}
}
}
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
@ -64,78 +31,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, thiserror::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))
} }
@ -144,7 +78,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()
})) }))

@ -17,6 +17,10 @@ rust-version.workspace = true
js = ["js-sys"] js = ["js-sys"]
custom-now = [] custom-now = []
[dependencies]
thiserror.workspace = true
wkt.workspace = true
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies]
js-sys = { workspace = true, optional = true } js-sys = { workspace = true, optional = true }

@ -2,7 +2,6 @@
use crate::{DayTimeDuration, Decimal, Duration, YearMonthDuration}; use crate::{DayTimeDuration, Decimal, Duration, YearMonthDuration};
use std::cmp::{min, Ordering}; use std::cmp::{min, Ordering};
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;
@ -2039,41 +2038,23 @@ fn time_on_timeline(props: &DateTimeSevenPropertyModel) -> Option<Decimal> {
} }
/// A parsing error /// A parsing error
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::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> {
@ -2326,11 +2307,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(ParseDateTimeError::InvalidTimezone)?,
kind: ParseDateTimeErrorKind::InvalidTimezone(e),
}
})?,
input, input,
)) ))
} }
@ -2400,9 +2378,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(())
} }
@ -2410,51 +2386,27 @@ fn validate_day_of_month(year: Option<i64>, month: u8, day: u8) -> Result<(), Pa
/// An overflow during [`DateTime`]-related operations. /// An overflow during [`DateTime`]-related operations.
/// ///
/// Matches XPath [`FODT0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0001). /// Matches XPath [`FODT0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0001).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("overflow during xsd:dateTime computation")]
pub struct DateTimeOverflowError; pub struct DateTimeOverflowError;
impl fmt::Display for DateTimeOverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("overflow during xsd:dateTime computation")
}
}
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).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("invalid timezone offset {}:{}",
self.offset_in_minutes / 60,
self.offset_in_minutes.abs() % 60)]
pub struct InvalidTimezoneError { pub struct InvalidTimezoneError {
offset_in_minutes: i64, offset_in_minutes: i64,
} }
impl fmt::Display for InvalidTimezoneError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid timezone offset {}:{}",
self.offset_in_minutes / 60,
self.offset_in_minutes.abs() % 60
)
}
}
impl Error for InvalidTimezoneError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]
use super::*; use super::*;
use std::error::Error;
#[test] #[test]
fn from_str() -> Result<(), ParseDateTimeError> { fn from_str() -> Result<(), ParseDateTimeError> {

@ -1,5 +1,4 @@
use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError}; use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError};
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;
@ -466,7 +465,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 +480,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 +492,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 +508,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,67 +608,31 @@ impl fmt::Display for Decimal {
} }
/// An error when parsing a [`Decimal`]. /// An error when parsing a [`Decimal`].
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::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,
}
} }
} }
/// The input is too large to fit into a [`Decimal`]. /// The input is too large to fit into a [`Decimal`].
/// ///
/// Matches XPath [`FOCA0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0001). /// Matches XPath [`FOCA0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0001).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Value too large for xsd:decimal internal representation")]
pub struct TooLargeForDecimalError; pub struct TooLargeForDecimalError;
impl fmt::Display for TooLargeForDecimalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Value too large for xsd:decimal internal representation")
}
}
impl Error for TooLargeForDecimalError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -1,6 +1,5 @@
use crate::{DateTime, Decimal}; use crate::{DateTime, Decimal};
use std::cmp::Ordering; use std::cmp::Ordering;
use std::error::Error;
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use std::time::Duration as StdDuration; use std::time::Duration as StdDuration;
@ -937,7 +936,8 @@ fn decimal_prefix(input: &str) -> (&str, &str) {
} }
/// A parsing error /// A parsing error
#[derive(Debug, Clone)] #[derive(Debug, Clone, thiserror::Error)]
#[error("{msg}")]
pub struct ParseDurationError { pub struct ParseDurationError {
msg: &'static str, msg: &'static str,
} }
@ -946,46 +946,24 @@ const OVERFLOW_ERROR: ParseDurationError = ParseDurationError {
msg: "Overflow error", msg: "Overflow error",
}; };
impl fmt::Display for ParseDurationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.msg)
}
}
impl ParseDurationError { impl ParseDurationError {
const fn msg(msg: &'static str) -> Self { const fn msg(msg: &'static str) -> Self {
Self { msg } Self { msg }
} }
} }
impl Error for ParseDurationError {}
/// An overflow during [`Duration`]-related operations. /// An overflow during [`Duration`]-related operations.
/// ///
/// Matches XPath [`FODT0002` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0002). /// Matches XPath [`FODT0002` error](https://www.w3.org/TR/xpath-functions-31/#ERRFODT0002).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("overflow during xsd:duration computation")]
pub struct DurationOverflowError; pub struct DurationOverflowError;
impl fmt::Display for DurationOverflowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("overflow during xsd:duration computation")
}
}
impl Error for DurationOverflowError {}
/// The year-month and the day-time components of a [`Duration`] have an opposite sign. /// The year-month and the day-time components of a [`Duration`] have an opposite sign.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("The xsd:yearMonthDuration and xsd:dayTimeDuration components of a xsd:duration can't have opposite sign")]
pub struct OppositeSignInDurationComponentsError; pub struct OppositeSignInDurationComponentsError;
impl fmt::Display for OppositeSignInDurationComponentsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("The xsd:yearMonthDuration and xsd:dayTimeDuration components of a xsd:duration can't have opposite sign")
}
}
impl Error for OppositeSignInDurationComponentsError {}
impl From<OppositeSignInDurationComponentsError> for ParseDurationError { impl From<OppositeSignInDurationComponentsError> for ParseDurationError {
#[inline] #[inline]
fn from(_: OppositeSignInDurationComponentsError) -> Self { fn from(_: OppositeSignInDurationComponentsError) -> Self {
@ -1000,6 +978,7 @@ mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]
use super::*; use super::*;
use std::error::Error;
#[test] #[test]
fn from_str() -> Result<(), ParseDurationError> { fn from_str() -> Result<(), ParseDurationError> {

@ -0,0 +1,77 @@
use std::fmt;
use std::fmt::Formatter;
use std::str::FromStr;
use thiserror::Error;
use wkt::types::{Coord, Point};
use wkt::{Geometry, Wkt};
// use std::time::Geo as StdDuration;
/// [XML Schema `duration` datatype](https://www.w3.org/TR/xmlschema11-2/#duration)
///
/// It stores the duration using a pair of a [`YearMonthDuration`] and a [`DayTimeDuration`].
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct GeoPoint {
pub x: f32,
pub y: f32,
}
#[derive(Error, Debug)]
pub enum GeoPointError {
#[error("Unable to parse WKT: {0}")]
WktParsingError(&'static str),
#[error("WKT type {0} is not supported")]
UnsupportedWktType(String),
}
impl GeoPoint {
#[inline]
pub fn new() -> Result<Self, GeoPointError> {
Self::from_str("POINT(0 0)")
}
#[inline]
pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
Self {
x: f32::from_be_bytes(bytes[0..4].try_into().unwrap()),
y: f32::from_be_bytes(bytes[4..8].try_into().unwrap()),
}
}
}
impl FromStr for GeoPoint {
type Err = GeoPointError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let geo = Wkt::from_str(input).map_err(GeoPointError::WktParsingError)?;
let Geometry::Point(Point(Some(Coord {
x,
y,
z: None,
m: None,
}))) = geo.item
else {
return Err(GeoPointError::UnsupportedWktType(geo.item.to_string()));
};
Ok(Self { x, y })
}
}
impl fmt::Display for GeoPoint {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "POINT({} {})", self.x, self.y)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::panic_in_result_fn)]
use super::*;
#[test]
fn from_str() {
let pt = GeoPoint::from_str("POINT(10 -20)").unwrap().0 .0.unwrap();
assert_eq!(pt.x, 10.0);
assert_eq!(pt.y, -20.0);
}
}

@ -1,5 +1,4 @@
use crate::{Boolean, Decimal, Double, Float}; use crate::{Boolean, Decimal, Double, Float};
use std::error::Error;
use std::fmt; use std::fmt;
use std::num::ParseIntError; use std::num::ParseIntError;
use std::str::FromStr; use std::str::FromStr;
@ -264,17 +263,10 @@ impl TryFrom<Double> for Integer {
/// The input is too large to fit into an [`Integer`]. /// The input is too large to fit into an [`Integer`].
/// ///
/// Matches XPath [`FOCA0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0003). /// Matches XPath [`FOCA0003` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0003).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, thiserror::Error)]
#[error("Value too large for xsd:integer internal representation")]
pub struct TooLargeForIntegerError; pub struct TooLargeForIntegerError;
impl fmt::Display for TooLargeForIntegerError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Value too large for xsd:integer internal representation")
}
}
impl Error for TooLargeForIntegerError {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::panic_in_result_fn)] #![allow(clippy::panic_in_result_fn)]

@ -10,6 +10,7 @@ mod decimal;
mod double; mod double;
mod duration; mod duration;
mod float; mod float;
mod geopoint;
mod integer; mod integer;
pub use self::boolean::Boolean; pub use self::boolean::Boolean;
@ -24,4 +25,5 @@ pub use self::duration::{
ParseDurationError, YearMonthDuration, ParseDurationError, YearMonthDuration,
}; };
pub use self::float::Float; pub use self::float::Float;
pub use self::geopoint::{GeoPoint, GeoPointError};
pub use self::integer::{Integer, TooLargeForIntegerError}; pub use self::integer::{Integer, TooLargeForIntegerError};

@ -23,6 +23,7 @@ memchr.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
oxiri.workspace = true oxiri.workspace = true
oxilangtag.workspace = true oxilangtag.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -1,4 +1,3 @@
use std::error::Error;
use std::ops::Range; use std::ops::Range;
use std::{fmt, io}; use std::{fmt, io};
@ -13,7 +12,7 @@ pub struct TextPosition {
/// An error in the syntax of the parsed file. /// An error in the syntax of the parsed file.
/// ///
/// It is composed of a message and a byte range in the input. /// It is composed of a message and a byte range in the input.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub struct SyntaxError { pub struct SyntaxError {
pub(super) location: Range<TextPosition>, pub(super) location: Range<TextPosition>,
pub(super) message: String, pub(super) message: String,
@ -67,8 +66,6 @@ impl fmt::Display for SyntaxError {
} }
} }
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 {
@ -79,46 +76,14 @@ impl From<SyntaxError> for io::Error {
/// A parsing error. /// A parsing error.
/// ///
/// It is the union of [`SyntaxError`] and [`io::Error`]. /// It is the union of [`SyntaxError`] and [`io::Error`].
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] SyntaxError),
impl fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Syntax(e) => e.fmt(f),
}
}
}
impl Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(match self {
Self::Io(e) => e,
Self::Syntax(e) => e,
})
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
}
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {

@ -23,6 +23,7 @@ json-event-parser.workspace = true
memchr.workspace = true memchr.workspace = true
oxrdf.workspace = true oxrdf.workspace = true
quick-xml.workspace = true quick-xml.workspace = true
thiserror.workspace = true
tokio = { workspace = true, optional = true, features = ["io-util"] } tokio = { workspace = true, optional = true, features = ["io-util"] }
[dev-dependencies] [dev-dependencies]

@ -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,8 +508,7 @@ 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 {
@ -525,7 +524,6 @@ impl<R: Read> TsvSolutionsReader<R> {
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,50 +1,17 @@
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};
/// Error returned during SPARQL result formats format parsing. /// Error returned during SPARQL result formats format parsing.
#[derive(Debug)] #[derive(Debug, thiserror::Error)]
pub enum ParseError { pub enum ParseError {
/// I/O error during parsing (file not found...). /// I/O error during parsing (file not found...).
Io(io::Error), #[error(transparent)]
Io(#[from] io::Error),
/// An error in the file syntax. /// An error in the file syntax.
Syntax(SyntaxError), #[error(transparent)]
} Syntax(#[from] SyntaxError),
impl fmt::Display for ParseError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
Self::Syntax(e) => e.fmt(f),
}
}
}
impl Error for ParseError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Io(e) => Some(e),
Self::Syntax(e) => Some(e),
}
}
}
impl From<io::Error> for ParseError {
#[inline]
fn from(error: io::Error) -> Self {
Self::Io(error)
}
}
impl From<SyntaxError> for ParseError {
#[inline]
fn from(error: SyntaxError) -> Self {
Self::Syntax(error)
}
} }
impl From<ParseError> for io::Error { impl From<ParseError> for io::Error {
@ -73,28 +40,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, thiserror::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>>,
@ -105,30 +70,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 {
@ -142,37 +103,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,
} }
} }
} }
@ -180,9 +113,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))
} }
@ -191,16 +124,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,21 @@ 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};
/// 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(ParseError::Parser)
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(ParseError::Parser)?;
inner: ParseErrorKind::Parser(e),
})?;
Ok(Update { Ok(Update {
operations, operations,
base_iri: state.base_iri, base_iri: state.base_iri,
@ -36,37 +31,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, thiserror::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 +667,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(ParseError::InvalidBaseIri)?)
inner: ParseErrorKind::InvalidBaseIri(e),
})?)
} else { } else {
None None
}, },

Loading…
Cancel
Save