oxirs_core/format/
error.rs

1//! Error handling for RDF format operations
2//!
3//! Extracted and adapted from OxiGraph error handling with OxiRS enhancements.
4
5use serde::{Deserialize, Serialize};
6use std::error::Error;
7use std::fmt;
8use std::io;
9
10/// Position in a text document
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct TextPosition {
13    /// Line number (1-based)
14    pub line: usize,
15    /// Column number (1-based)
16    pub column: usize,
17    /// Byte offset from start of document
18    pub offset: usize,
19}
20
21impl TextPosition {
22    /// Create a new text position
23    pub fn new(line: usize, column: usize, offset: usize) -> Self {
24        Self {
25            line,
26            column,
27            offset,
28        }
29    }
30
31    /// Position at start of document
32    pub fn start() -> Self {
33        Self::new(1, 1, 0)
34    }
35}
36
37impl fmt::Display for TextPosition {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "line {}, column {}", self.line, self.column)
40    }
41}
42
43/// Syntax error during RDF parsing
44#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
45pub struct RdfSyntaxError {
46    /// Error message
47    pub message: String,
48    /// Position where error occurred
49    pub position: Option<TextPosition>,
50    /// Context around the error (line content)
51    pub context: Option<String>,
52}
53
54impl RdfSyntaxError {
55    /// Create a new syntax error
56    pub fn new(message: impl Into<String>) -> Self {
57        Self {
58            message: message.into(),
59            position: None,
60            context: None,
61        }
62    }
63
64    /// Create a syntax error with position
65    pub fn with_position(message: impl Into<String>, position: TextPosition) -> Self {
66        Self {
67            message: message.into(),
68            position: Some(position),
69            context: None,
70        }
71    }
72
73    /// Create a syntax error with position and context
74    pub fn with_context(
75        message: impl Into<String>,
76        position: TextPosition,
77        context: impl Into<String>,
78    ) -> Self {
79        Self {
80            message: message.into(),
81            position: Some(position),
82            context: Some(context.into()),
83        }
84    }
85
86    /// Add position information to the error
87    pub fn at_position(mut self, position: TextPosition) -> Self {
88        self.position = Some(position);
89        self
90    }
91
92    /// Add context information to the error
93    pub fn with_context_str(mut self, context: impl Into<String>) -> Self {
94        self.context = Some(context.into());
95        self
96    }
97}
98
99impl fmt::Display for RdfSyntaxError {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        write!(f, "Syntax error: {}", self.message)?;
102
103        if let Some(position) = &self.position {
104            write!(f, " at {position}")?;
105        }
106
107        if let Some(context) = &self.context {
108            write!(f, "\nContext: {context}")?;
109        }
110
111        Ok(())
112    }
113}
114
115impl Error for RdfSyntaxError {}
116
117/// Parse error during RDF processing  
118#[derive(Debug)]
119pub enum RdfParseError {
120    /// Syntax error in the RDF document
121    Syntax(RdfSyntaxError),
122    /// I/O error during reading
123    Io(io::Error),
124    /// Invalid IRI format
125    InvalidIri(String),
126    /// Invalid literal format
127    InvalidLiteral(String),
128    /// Invalid blank node format
129    InvalidBlankNode(String),
130    /// Invalid datatype
131    InvalidDatatype(String),
132    /// Invalid language tag
133    InvalidLanguageTag(String),
134    /// Unsupported feature
135    UnsupportedFeature(String),
136    /// Internal processing error
137    Internal(String),
138}
139
140impl RdfParseError {
141    /// Create a syntax error
142    pub fn syntax(message: impl Into<String>) -> Self {
143        Self::Syntax(RdfSyntaxError::new(message))
144    }
145
146    /// Create a syntax error with position
147    pub fn syntax_at(message: impl Into<String>, position: TextPosition) -> Self {
148        Self::Syntax(RdfSyntaxError::with_position(message, position))
149    }
150
151    /// Create an invalid IRI error
152    pub fn invalid_iri(iri: impl Into<String>) -> Self {
153        Self::InvalidIri(iri.into())
154    }
155
156    /// Create an invalid literal error
157    pub fn invalid_literal(literal: impl Into<String>) -> Self {
158        Self::InvalidLiteral(literal.into())
159    }
160
161    /// Create an unsupported feature error
162    pub fn unsupported(feature: impl Into<String>) -> Self {
163        Self::UnsupportedFeature(feature.into())
164    }
165
166    /// Create an internal error
167    pub fn internal(message: impl Into<String>) -> Self {
168        Self::Internal(message.into())
169    }
170}
171
172impl fmt::Display for RdfParseError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            Self::Syntax(err) => write!(f, "{err}"),
176            Self::Io(err) => write!(f, "I/O error: {err}"),
177            Self::InvalidIri(iri) => write!(f, "Invalid IRI: {iri}"),
178            Self::InvalidLiteral(literal) => write!(f, "Invalid literal: {literal}"),
179            Self::InvalidBlankNode(bnode) => write!(f, "Invalid blank node: {bnode}"),
180            Self::InvalidDatatype(datatype) => write!(f, "Invalid datatype: {datatype}"),
181            Self::InvalidLanguageTag(tag) => write!(f, "Invalid language tag: {tag}"),
182            Self::UnsupportedFeature(feature) => write!(f, "Unsupported feature: {feature}"),
183            Self::Internal(msg) => write!(f, "Internal error: {msg}"),
184        }
185    }
186}
187
188impl Error for RdfParseError {
189    fn source(&self) -> Option<&(dyn Error + 'static)> {
190        match self {
191            Self::Syntax(err) => Some(err),
192            Self::Io(err) => Some(err),
193            _ => None,
194        }
195    }
196}
197
198impl From<io::Error> for RdfParseError {
199    fn from(err: io::Error) -> Self {
200        Self::Io(err)
201    }
202}
203
204impl From<RdfSyntaxError> for RdfParseError {
205    fn from(err: RdfSyntaxError) -> Self {
206        Self::Syntax(err)
207    }
208}
209
210impl From<crate::OxirsError> for RdfParseError {
211    fn from(err: crate::OxirsError) -> Self {
212        match err {
213            crate::OxirsError::Parse(msg) => Self::syntax(msg),
214            crate::OxirsError::Io(msg) => Self::syntax(format!("IO error: {msg}")),
215            crate::OxirsError::Store(msg) => Self::internal(format!("Store error: {msg}")),
216            crate::OxirsError::Query(msg) => Self::internal(format!("Query error: {msg}")),
217            crate::OxirsError::Serialize(msg) => {
218                Self::internal(format!("Serialization error: {msg}"))
219            }
220            crate::OxirsError::QuantumError(msg) => Self::internal(format!("Quantum error: {msg}")),
221            crate::OxirsError::MolecularError(msg) => {
222                Self::internal(format!("Molecular error: {msg}"))
223            }
224            crate::OxirsError::NeuralSymbolicError(msg) => {
225                Self::internal(format!("Neural-symbolic error: {msg}"))
226            }
227            crate::OxirsError::ConcurrencyError(msg) => {
228                Self::internal(format!("Concurrency error: {msg}"))
229            }
230            crate::OxirsError::NotSupported(msg) => {
231                Self::unsupported(format!("Not supported: {msg}"))
232            }
233            crate::OxirsError::Update(msg) => Self::internal(format!("Update error: {msg}")),
234            crate::OxirsError::Federation(msg) => {
235                Self::internal(format!("Federation error: {msg}"))
236            }
237        }
238    }
239}
240
241// Add direct conversion from LanguageTagParseError to RdfParseError
242impl From<crate::model::literal::LanguageTagParseError> for RdfParseError {
243    fn from(err: crate::model::literal::LanguageTagParseError) -> Self {
244        Self::InvalidLanguageTag(err.to_string())
245    }
246}
247
248/// General format error for high-level operations
249#[derive(Debug)]
250pub enum FormatError {
251    /// Parse error
252    Parse(RdfParseError),
253    /// Serialization error
254    Serialize(io::Error),
255    /// Unsupported format
256    UnsupportedFormat(String),
257    /// Invalid data
258    InvalidData(String),
259    /// Missing required component
260    MissingComponent(String),
261    /// Configuration error
262    Configuration(String),
263}
264
265impl FormatError {
266    /// Create an unsupported format error
267    pub fn unsupported_format(format: impl Into<String>) -> Self {
268        Self::UnsupportedFormat(format.into())
269    }
270
271    /// Create an invalid data error
272    pub fn invalid_data(message: impl Into<String>) -> Self {
273        Self::InvalidData(message.into())
274    }
275
276    /// Create a missing component error
277    pub fn missing_component(component: impl Into<String>) -> Self {
278        Self::MissingComponent(component.into())
279    }
280
281    /// Create a configuration error
282    pub fn configuration(message: impl Into<String>) -> Self {
283        Self::Configuration(message.into())
284    }
285}
286
287impl fmt::Display for FormatError {
288    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
289        match self {
290            Self::Parse(err) => write!(f, "Parse error: {err}"),
291            Self::Serialize(err) => write!(f, "Serialization error: {err}"),
292            Self::UnsupportedFormat(format) => write!(f, "Unsupported format: {format}"),
293            Self::InvalidData(msg) => write!(f, "Invalid data: {msg}"),
294            Self::MissingComponent(component) => write!(f, "Missing component: {component}"),
295            Self::Configuration(msg) => write!(f, "Configuration error: {msg}"),
296        }
297    }
298}
299
300impl Error for FormatError {
301    fn source(&self) -> Option<&(dyn Error + 'static)> {
302        match self {
303            Self::Parse(err) => Some(err),
304            Self::Serialize(err) => Some(err),
305            _ => None,
306        }
307    }
308}
309
310impl From<RdfParseError> for FormatError {
311    fn from(err: RdfParseError) -> Self {
312        Self::Parse(err)
313    }
314}
315
316impl From<io::Error> for FormatError {
317    fn from(err: io::Error) -> Self {
318        Self::Serialize(err)
319    }
320}
321
322impl From<RdfSyntaxError> for FormatError {
323    fn from(err: RdfSyntaxError) -> Self {
324        Self::Parse(err.into())
325    }
326}
327
328/// Result type for parsing operations
329pub type ParseResult<T> = Result<T, RdfParseError>;
330
331/// Result type for serialization operations  
332pub type SerializeResult<T> = Result<T, io::Error>;
333
334/// Result type for general format operations
335pub type FormatResult<T> = Result<T, FormatError>;
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340
341    #[test]
342    fn test_text_position() {
343        let pos = TextPosition::new(10, 5, 100);
344        assert_eq!(pos.line, 10);
345        assert_eq!(pos.column, 5);
346        assert_eq!(pos.offset, 100);
347
348        let start = TextPosition::start();
349        assert_eq!(start.line, 1);
350        assert_eq!(start.column, 1);
351        assert_eq!(start.offset, 0);
352    }
353
354    #[test]
355    fn test_syntax_error() {
356        let err = RdfSyntaxError::new("Invalid syntax");
357        assert_eq!(err.message, "Invalid syntax");
358        assert!(err.position.is_none());
359
360        let pos = TextPosition::new(5, 10, 50);
361        let err_with_pos = RdfSyntaxError::with_position("Bad token", pos);
362        assert_eq!(err_with_pos.position, Some(pos));
363    }
364
365    #[test]
366    fn test_parse_error() {
367        let syntax_err = RdfParseError::syntax("Bad syntax");
368        assert!(matches!(syntax_err, RdfParseError::Syntax(_)));
369
370        let iri_err = RdfParseError::invalid_iri("not-an-iri");
371        assert!(matches!(iri_err, RdfParseError::InvalidIri(_)));
372
373        let unsupported_err = RdfParseError::unsupported("Some feature");
374        assert!(matches!(
375            unsupported_err,
376            RdfParseError::UnsupportedFeature(_)
377        ));
378    }
379
380    #[test]
381    fn test_format_error() {
382        let format_err = FormatError::unsupported_format("unknown/format");
383        assert!(matches!(format_err, FormatError::UnsupportedFormat(_)));
384
385        let data_err = FormatError::invalid_data("Bad data");
386        assert!(matches!(data_err, FormatError::InvalidData(_)));
387    }
388
389    #[test]
390    fn test_error_conversion() {
391        let syntax_err = RdfSyntaxError::new("Bad syntax");
392        let parse_err: RdfParseError = syntax_err.into();
393        let format_err: FormatError = parse_err.into();
394
395        assert!(matches!(format_err, FormatError::Parse(_)));
396    }
397}