use serde::{Deserialize, Serialize};
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TextPosition {
pub line: usize,
pub column: usize,
pub offset: usize,
}
impl TextPosition {
pub fn new(line: usize, column: usize, offset: usize) -> Self {
Self {
line,
column,
offset,
}
}
pub fn start() -> Self {
Self::new(1, 1, 0)
}
}
impl fmt::Display for TextPosition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "line {}, column {}", self.line, self.column)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RdfSyntaxError {
pub message: String,
pub position: Option<TextPosition>,
pub context: Option<String>,
}
impl RdfSyntaxError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
position: None,
context: None,
}
}
pub fn with_position(message: impl Into<String>, position: TextPosition) -> Self {
Self {
message: message.into(),
position: Some(position),
context: None,
}
}
pub fn with_context(
message: impl Into<String>,
position: TextPosition,
context: impl Into<String>,
) -> Self {
Self {
message: message.into(),
position: Some(position),
context: Some(context.into()),
}
}
pub fn at_position(mut self, position: TextPosition) -> Self {
self.position = Some(position);
self
}
pub fn with_context_str(mut self, context: impl Into<String>) -> Self {
self.context = Some(context.into());
self
}
}
impl fmt::Display for RdfSyntaxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Syntax error: {}", self.message)?;
if let Some(position) = &self.position {
write!(f, " at {position}")?;
}
if let Some(context) = &self.context {
write!(f, "\nContext: {context}")?;
}
Ok(())
}
}
impl Error for RdfSyntaxError {}
#[derive(Debug)]
pub enum RdfParseError {
Syntax(RdfSyntaxError),
Io(io::Error),
InvalidIri(String),
InvalidLiteral(String),
InvalidBlankNode(String),
InvalidDatatype(String),
InvalidLanguageTag(String),
UnsupportedFeature(String),
Internal(String),
}
impl RdfParseError {
pub fn syntax(message: impl Into<String>) -> Self {
Self::Syntax(RdfSyntaxError::new(message))
}
pub fn syntax_at(message: impl Into<String>, position: TextPosition) -> Self {
Self::Syntax(RdfSyntaxError::with_position(message, position))
}
pub fn invalid_iri(iri: impl Into<String>) -> Self {
Self::InvalidIri(iri.into())
}
pub fn invalid_literal(literal: impl Into<String>) -> Self {
Self::InvalidLiteral(literal.into())
}
pub fn unsupported(feature: impl Into<String>) -> Self {
Self::UnsupportedFeature(feature.into())
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal(message.into())
}
}
impl fmt::Display for RdfParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Syntax(err) => write!(f, "{err}"),
Self::Io(err) => write!(f, "I/O error: {err}"),
Self::InvalidIri(iri) => write!(f, "Invalid IRI: {iri}"),
Self::InvalidLiteral(literal) => write!(f, "Invalid literal: {literal}"),
Self::InvalidBlankNode(bnode) => write!(f, "Invalid blank node: {bnode}"),
Self::InvalidDatatype(datatype) => write!(f, "Invalid datatype: {datatype}"),
Self::InvalidLanguageTag(tag) => write!(f, "Invalid language tag: {tag}"),
Self::UnsupportedFeature(feature) => write!(f, "Unsupported feature: {feature}"),
Self::Internal(msg) => write!(f, "Internal error: {msg}"),
}
}
}
impl Error for RdfParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Syntax(err) => Some(err),
Self::Io(err) => Some(err),
_ => None,
}
}
}
impl From<io::Error> for RdfParseError {
fn from(err: io::Error) -> Self {
Self::Io(err)
}
}
impl From<RdfSyntaxError> for RdfParseError {
fn from(err: RdfSyntaxError) -> Self {
Self::Syntax(err)
}
}
impl From<crate::OxirsError> for RdfParseError {
fn from(err: crate::OxirsError) -> Self {
match err {
crate::OxirsError::Parse(msg) => Self::syntax(msg),
crate::OxirsError::Io(msg) => Self::syntax(format!("IO error: {msg}")),
crate::OxirsError::Store(msg) => Self::internal(format!("Store error: {msg}")),
crate::OxirsError::Query(msg) => Self::internal(format!("Query error: {msg}")),
crate::OxirsError::Serialize(msg) => {
Self::internal(format!("Serialization error: {msg}"))
}
crate::OxirsError::QuantumError(msg) => Self::internal(format!("Quantum error: {msg}")),
crate::OxirsError::MolecularError(msg) => {
Self::internal(format!("Molecular error: {msg}"))
}
crate::OxirsError::NeuralSymbolicError(msg) => {
Self::internal(format!("Neural-symbolic error: {msg}"))
}
crate::OxirsError::ConcurrencyError(msg) => {
Self::internal(format!("Concurrency error: {msg}"))
}
crate::OxirsError::NotSupported(msg) => {
Self::unsupported(format!("Not supported: {msg}"))
}
crate::OxirsError::Update(msg) => Self::internal(format!("Update error: {msg}")),
crate::OxirsError::Federation(msg) => {
Self::internal(format!("Federation error: {msg}"))
}
}
}
}
impl From<crate::model::literal::LanguageTagParseError> for RdfParseError {
fn from(err: crate::model::literal::LanguageTagParseError) -> Self {
Self::InvalidLanguageTag(err.to_string())
}
}
#[derive(Debug)]
pub enum FormatError {
Parse(RdfParseError),
Serialize(io::Error),
UnsupportedFormat(String),
InvalidData(String),
MissingComponent(String),
Configuration(String),
}
impl FormatError {
pub fn unsupported_format(format: impl Into<String>) -> Self {
Self::UnsupportedFormat(format.into())
}
pub fn invalid_data(message: impl Into<String>) -> Self {
Self::InvalidData(message.into())
}
pub fn missing_component(component: impl Into<String>) -> Self {
Self::MissingComponent(component.into())
}
pub fn configuration(message: impl Into<String>) -> Self {
Self::Configuration(message.into())
}
}
impl fmt::Display for FormatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parse(err) => write!(f, "Parse error: {err}"),
Self::Serialize(err) => write!(f, "Serialization error: {err}"),
Self::UnsupportedFormat(format) => write!(f, "Unsupported format: {format}"),
Self::InvalidData(msg) => write!(f, "Invalid data: {msg}"),
Self::MissingComponent(component) => write!(f, "Missing component: {component}"),
Self::Configuration(msg) => write!(f, "Configuration error: {msg}"),
}
}
}
impl Error for FormatError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Parse(err) => Some(err),
Self::Serialize(err) => Some(err),
_ => None,
}
}
}
impl From<RdfParseError> for FormatError {
fn from(err: RdfParseError) -> Self {
Self::Parse(err)
}
}
impl From<io::Error> for FormatError {
fn from(err: io::Error) -> Self {
Self::Serialize(err)
}
}
impl From<RdfSyntaxError> for FormatError {
fn from(err: RdfSyntaxError) -> Self {
Self::Parse(err.into())
}
}
pub type ParseResult<T> = Result<T, RdfParseError>;
pub type SerializeResult<T> = Result<T, io::Error>;
pub type FormatResult<T> = Result<T, FormatError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_text_position() {
let pos = TextPosition::new(10, 5, 100);
assert_eq!(pos.line, 10);
assert_eq!(pos.column, 5);
assert_eq!(pos.offset, 100);
let start = TextPosition::start();
assert_eq!(start.line, 1);
assert_eq!(start.column, 1);
assert_eq!(start.offset, 0);
}
#[test]
fn test_syntax_error() {
let err = RdfSyntaxError::new("Invalid syntax");
assert_eq!(err.message, "Invalid syntax");
assert!(err.position.is_none());
let pos = TextPosition::new(5, 10, 50);
let err_with_pos = RdfSyntaxError::with_position("Bad token", pos);
assert_eq!(err_with_pos.position, Some(pos));
}
#[test]
fn test_parse_error() {
let syntax_err = RdfParseError::syntax("Bad syntax");
assert!(matches!(syntax_err, RdfParseError::Syntax(_)));
let iri_err = RdfParseError::invalid_iri("not-an-iri");
assert!(matches!(iri_err, RdfParseError::InvalidIri(_)));
let unsupported_err = RdfParseError::unsupported("Some feature");
assert!(matches!(
unsupported_err,
RdfParseError::UnsupportedFeature(_)
));
}
#[test]
fn test_format_error() {
let format_err = FormatError::unsupported_format("unknown/format");
assert!(matches!(format_err, FormatError::UnsupportedFormat(_)));
let data_err = FormatError::invalid_data("Bad data");
assert!(matches!(data_err, FormatError::InvalidData(_)));
}
#[test]
fn test_error_conversion() {
let syntax_err = RdfSyntaxError::new("Bad syntax");
let parse_err: RdfParseError = syntax_err.into();
let format_err: FormatError = parse_err.into();
assert!(matches!(format_err, FormatError::Parse(_)));
}
}