oslquery_petite/parser/
mod.rs1pub mod hint;
9pub mod oso;
11pub mod reader;
13pub mod types;
15
16pub use reader::OsoReader;
17
18use ariadne::{Color, Label, Report, ReportKind, Source};
19use thiserror::Error;
20
21#[derive(Debug, Clone, Error, PartialEq, Eq)]
27pub enum ParseError {
28 #[error("IO error: {0}")]
29 Io(String),
30
31 #[error("Invalid OSO file format: {0}")]
32 InvalidFormat(String),
33
34 #[error("Unsupported OSO version: {major}.{minor}")]
35 UnsupportedVersion { major: i32, minor: i32 },
36
37 #[error("Parse error at line {line}: {message}")]
38 ParseError {
39 line: usize,
40 message: String,
41 token_info: Option<(String, usize)>,
43 },
44
45 #[error("Incomplete parse: {0}")]
46 Incomplete(String),
47
48 #[error("Conversion error: {0}")]
49 Conversion(String),
50}
51
52#[cfg(feature = "hash")]
54impl std::hash::Hash for ParseError {
55 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
56 std::mem::discriminant(self).hash(state);
57 match self {
58 ParseError::Io(s) => s.hash(state),
59 ParseError::InvalidFormat(s) => s.hash(state),
60 ParseError::UnsupportedVersion { major, minor } => {
61 major.hash(state);
62 minor.hash(state);
63 }
64 ParseError::ParseError {
65 line,
66 message,
67 token_info,
68 } => {
69 line.hash(state);
70 message.hash(state);
71 token_info.hash(state);
72 }
73 ParseError::Incomplete(s) => s.hash(state),
74 ParseError::Conversion(s) => s.hash(state),
75 }
76 }
77}
78
79impl From<std::io::Error> for ParseError {
81 fn from(err: std::io::Error) -> Self {
82 ParseError::Io(err.to_string())
83 }
84}
85
86impl ParseError {
87 pub fn print_with_source(&self, filename: &str, source: &str) -> std::io::Result<()> {
89 match self {
90 ParseError::ParseError {
91 line,
92 message,
93 token_info,
94 } => {
95 let mut line_start_offset = 0;
97 let mut current_line = 1;
98 for (i, ch) in source.char_indices() {
99 if current_line == *line {
100 line_start_offset = i;
101 break;
102 }
103 if ch == '\n' {
104 current_line += 1;
105 }
106 }
107
108 let line_content = source[line_start_offset..].lines().next().unwrap_or("");
110
111 let (start_offset, end_offset) = if let Some((token, _token_pos)) = token_info {
113 if let Some(token_idx) = line_content.find(token.as_str()) {
115 let token_start = line_start_offset + token_idx;
116 let token_end = token_start + token.len();
117 (token_start, token_end)
118 } else {
119 let line_end = line_start_offset + line_content.len();
121 (line_start_offset, line_end)
122 }
123 } else {
124 let line_end = line_start_offset + line_content.len();
126 (line_start_offset, line_end)
127 };
128
129 Report::build(ReportKind::Error, (filename, start_offset..end_offset))
130 .with_message(format!("Parse error: {}", message))
131 .with_label(
132 Label::new((filename, start_offset..end_offset))
133 .with_message(message)
134 .with_color(Color::Red),
135 )
136 .finish()
137 .print((filename, Source::from(source)))
138 }
139 ParseError::UnsupportedVersion { major, minor } => {
140 Report::build(ReportKind::Error, (filename, 0..0))
141 .with_message(format!("Unsupported OSO version: {}.{}", major, minor))
142 .with_note("This parser supports OSO version 1.11 and above")
143 .finish()
144 .print((filename, Source::from(source)))
145 }
146 _ => {
147 eprintln!("Error: {}", self);
149 Ok(())
150 }
151 }
152 }
153}