Skip to main content

tensorlogic_oxirs_bridge/
error.rs

1//! Error types for the OxiRS bridge.
2
3use thiserror::Error;
4
5/// Parse location information for better error reporting
6#[derive(Debug, Clone)]
7pub struct ParseLocation {
8    pub line: usize,
9    pub column: usize,
10    pub context: Option<String>,
11}
12
13impl ParseLocation {
14    pub fn new(line: usize, column: usize) -> Self {
15        Self {
16            line,
17            column,
18            context: None,
19        }
20    }
21
22    pub fn with_context(mut self, context: String) -> Self {
23        self.context = Some(context);
24        self
25    }
26}
27
28impl std::fmt::Display for ParseLocation {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "line {}, column {}", self.line, self.column)?;
31        if let Some(ctx) = &self.context {
32            write!(f, "\n  {}", ctx)?;
33        }
34        Ok(())
35    }
36}
37
38#[derive(Error, Debug)]
39pub enum BridgeError {
40    #[error("Invalid RDF schema: {0}")]
41    InvalidSchema(String),
42
43    #[error("Parse error at {location}: {message}")]
44    ParseError {
45        location: ParseLocation,
46        message: String,
47    },
48
49    #[error("SHACL shape not supported: {0}")]
50    UnsupportedShape(String),
51
52    #[error("Property '{property}' not found in schema")]
53    PropertyNotFound { property: String },
54
55    #[error("Class '{class}' not found in schema")]
56    ClassNotFound { class: String },
57
58    #[error("Provenance tracking error: {0}")]
59    ProvenanceError(String),
60
61    #[error("Invalid IRI '{iri}': {reason}")]
62    InvalidIri { iri: String, reason: String },
63
64    #[error("Missing required field '{field}' in {context}")]
65    MissingField { field: String, context: String },
66
67    #[error("Validation error: {0}")]
68    ValidationError(String),
69
70    #[error("Index error: {0}")]
71    IndexError(String),
72
73    #[error("Cache error: {0}")]
74    CacheError(String),
75}
76
77impl BridgeError {
78    /// Create a parse error with location
79    pub fn parse_error(line: usize, column: usize, message: impl Into<String>) -> Self {
80        Self::ParseError {
81            location: ParseLocation::new(line, column),
82            message: message.into(),
83        }
84    }
85
86    /// Create a parse error with location and context
87    pub fn parse_error_with_context(
88        line: usize,
89        column: usize,
90        message: impl Into<String>,
91        context: impl Into<String>,
92    ) -> Self {
93        Self::ParseError {
94            location: ParseLocation::new(line, column).with_context(context.into()),
95            message: message.into(),
96        }
97    }
98
99    /// Suggest a fix for this error
100    pub fn suggestion(&self) -> Option<String> {
101        match self {
102            Self::PropertyNotFound { property } => {
103                Some(format!("Did you mean to define a property with IRI '{}'? Add it to your RDF schema with 'a rdf:Property'.", property))
104            }
105            Self::ClassNotFound { class } => {
106                Some(format!("Did you mean to define a class with IRI '{}'? Add it to your RDF schema with 'a rdfs:Class'.", class))
107            }
108            Self::InvalidIri { iri, reason } => {
109                Some(format!("Ensure '{}' is a valid IRI. Reason: {}", iri, reason))
110            }
111            Self::MissingField { field, context } => {
112                Some(format!("Add the required field '{}' to {}", field, context))
113            }
114            _ => None,
115        }
116    }
117}