1use std::fmt;
7use thiserror::Error;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct TextPosition {
12 pub line: usize,
14 pub column: usize,
16 pub offset: usize,
18}
19
20impl Default for TextPosition {
21 fn default() -> Self {
22 Self::start()
23 }
24}
25
26impl TextPosition {
27 pub fn new(line: usize, column: usize, offset: usize) -> Self {
29 Self {
30 line,
31 column,
32 offset,
33 }
34 }
35
36 pub fn start() -> Self {
38 Self::new(1, 1, 0)
39 }
40
41 pub fn advance_char(&mut self, ch: char) {
43 if ch == '\n' {
44 self.line += 1;
45 self.column = 1;
46 } else {
47 self.column += 1;
48 }
49 self.offset += ch.len_utf8();
50 }
51
52 pub fn advance_bytes(&mut self, bytes: &[u8]) {
54 for &byte in bytes {
55 if byte == b'\n' {
56 self.line += 1;
57 self.column = 1;
58 } else if byte >= 0x20 || byte == b'\t' {
59 self.column += 1;
60 }
61 self.offset += 1;
62 }
63 }
64}
65
66impl fmt::Display for TextPosition {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 write!(f, "line {}, column {}", self.line, self.column)
69 }
70}
71
72#[derive(Debug, Clone, Error)]
74pub enum TurtleSyntaxError {
75 #[error("Unexpected character '{character}' at {position}")]
77 UnexpectedCharacter {
78 character: char,
80 position: TextPosition,
82 },
83
84 #[error("Unexpected end of input at {position}")]
86 UnexpectedEof {
87 position: TextPosition,
89 },
90
91 #[error("Invalid IRI '{iri}' at {position}: {reason}")]
93 InvalidIri {
94 iri: String,
96 reason: String,
98 position: TextPosition,
100 },
101
102 #[error("Invalid language tag '{tag}' at {position}: {reason}")]
104 InvalidLanguageTag {
105 tag: String,
107 reason: String,
109 position: TextPosition,
111 },
112
113 #[error("Invalid literal '{literal}' at {position}: {reason}")]
115 InvalidLiteral {
116 literal: String,
118 reason: String,
120 position: TextPosition,
122 },
123
124 #[error("Invalid escape sequence '\\{sequence}' at {position}")]
126 InvalidEscape {
127 sequence: String,
129 position: TextPosition,
131 },
132
133 #[error("Invalid Unicode code point U+{codepoint:04X} at {position}")]
135 InvalidUnicode {
136 codepoint: u32,
138 position: TextPosition,
140 },
141
142 #[error("Invalid blank node identifier '{id}' at {position}")]
144 InvalidBlankNode {
145 id: String,
147 position: TextPosition,
149 },
150
151 #[error("Undefined prefix '{prefix}' at {position}")]
153 UndefinedPrefix {
154 prefix: String,
156 position: TextPosition,
158 },
159
160 #[error("Invalid prefix declaration for '{prefix}' at {position}: {reason}")]
162 InvalidPrefix {
163 prefix: String,
165 reason: String,
167 position: TextPosition,
169 },
170
171 #[error("Invalid base IRI '{iri}' at {position}: {reason}")]
173 InvalidBase {
174 iri: String,
176 reason: String,
178 position: TextPosition,
180 },
181
182 #[error("Syntax error at {position}: {message}")]
184 Generic {
185 message: String,
187 position: TextPosition,
189 },
190}
191
192#[derive(Debug, Error)]
194pub enum TurtleParseError {
195 #[error("Syntax error: {0}")]
197 Syntax(#[from] TurtleSyntaxError),
198
199 #[error("I/O error: {0}")]
201 Io(#[from] std::io::Error),
202
203 #[error("RDF model error: {0}")]
205 Model(#[from] oxirs_core::OxirsError),
206
207 #[error("Multiple errors occurred ({} errors)", .errors.len())]
209 Multiple {
210 errors: Vec<TurtleParseError>,
212 },
213}
214
215pub type TurtleResult<T> = Result<T, TurtleParseError>;
217
218#[derive(Debug, Clone, Error)]
220pub enum TokenRecognizerError {
221 #[error("Unexpected character: {0}")]
223 UnexpectedCharacter(char),
224
225 #[error("Unexpected end of input")]
227 UnexpectedEof,
228
229 #[error("Invalid token: {0}")]
231 Invalid(String),
232}
233
234#[derive(Debug, Clone, Error)]
236pub enum RuleRecognizerError {
237 #[error("Unexpected token: {0}")]
239 UnexpectedToken(String),
240
241 #[error("Missing required token: {0}")]
243 MissingToken(String),
244
245 #[error("Invalid rule: {0}")]
247 InvalidRule(String),
248}
249
250impl TurtleParseError {
251 pub fn syntax(error: TurtleSyntaxError) -> Self {
253 Self::Syntax(error)
254 }
255
256 pub fn io(error: std::io::Error) -> Self {
258 Self::Io(error)
259 }
260
261 pub fn model(error: oxirs_core::OxirsError) -> Self {
263 Self::Model(error)
264 }
265
266 pub fn multiple(errors: Vec<TurtleParseError>) -> Self {
268 Self::Multiple { errors }
269 }
270
271 pub fn position(&self) -> Option<TextPosition> {
273 match self {
274 Self::Syntax(syntax_error) => Some(syntax_error.position()),
275 _ => None,
276 }
277 }
278}
279
280impl TurtleSyntaxError {
281 pub fn position(&self) -> TextPosition {
283 match self {
284 Self::UnexpectedCharacter { position, .. } => *position,
285 Self::UnexpectedEof { position } => *position,
286 Self::InvalidIri { position, .. } => *position,
287 Self::InvalidLanguageTag { position, .. } => *position,
288 Self::InvalidLiteral { position, .. } => *position,
289 Self::InvalidEscape { position, .. } => *position,
290 Self::InvalidUnicode { position, .. } => *position,
291 Self::InvalidBlankNode { position, .. } => *position,
292 Self::UndefinedPrefix { position, .. } => *position,
293 Self::InvalidPrefix { position, .. } => *position,
294 Self::InvalidBase { position, .. } => *position,
295 Self::Generic { position, .. } => *position,
296 }
297 }
298}