Skip to content

Commit e126aae

Browse files
committed
refactor(fieldmask): better path parser error
1 parent 3e5cc97 commit e126aae

File tree

4 files changed

+89
-11
lines changed

4 files changed

+89
-11
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pilota-thrift-fieldmask/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pilota-thrift-parser = { path = "../pilota-thrift-parser", version = "0.12" }
2424
pilota-thrift-reflect = { path = "../pilota-thrift-reflect", version = "0.1" }
2525

2626
ahash.workspace = true
27+
ariadne.workspace = true
2728
faststr.workspace = true
2829
thiserror.workspace = true
2930
serde.workspace = true

pilota-thrift-fieldmask/src/fieldmask.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,12 +1580,6 @@ mod tests {
15801580

15811581
#[test]
15821582
fn test_field_mask_error_display() {
1583-
let err = FieldMaskError::PathError {
1584-
path: FastStr::new("$.invalid"),
1585-
source: Box::new(PathError::SyntaxError { position: 5 }),
1586-
};
1587-
assert!(err.to_string().contains("path '$.invalid', parse error"));
1588-
15891583
let err = FieldMaskError::TypeMismatch {
15901584
detail: Box::new(TypeMismatchDetail {
15911585
expected: "Struct".into(),

pilota-thrift-fieldmask/src/path.rs

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use std::{fmt, str};
22

3+
use ariadne::{Color, Label, Report, ReportKind, Source};
34
use chumsky::prelude::*;
45
use pilota::FastStr;
56
use pilota_thrift_parser::descriptor::Components;
67
use thiserror::Error;
78

89
#[derive(Debug, Clone, Error)]
910
pub enum PathError {
10-
#[error("syntax error at position {position}")]
11-
SyntaxError { position: usize },
11+
#[error("syntax error: {message}")]
12+
SyntaxError { message: FastStr },
1213
#[error("unexpected EOF")]
1314
UnexpectedEof,
1415
#[error("path cannot be empty")]
@@ -219,9 +220,42 @@ impl PathIterator {
219220
.parse(src.as_ref())
220221
.into_output_errors();
221222
if !errs.is_empty() {
222-
return Err(PathError::ParseError {
223-
position: errs[0].span().start,
224-
message: errs[0].to_string().into(),
223+
let mut report_strings = Vec::with_capacity(errs.len() + 1);
224+
225+
let title = format!("{} errors found: ", errs.len());
226+
report_strings.push(title);
227+
report_strings.push(String::new());
228+
229+
for (i, e) in errs.iter().enumerate() {
230+
if errs.len() > 1 {
231+
let error_header = format!("Error {}:", i + 1);
232+
report_strings.push(error_header.clone());
233+
}
234+
235+
let mut buffer = Vec::new();
236+
Report::build(ReportKind::Error, e.span().into_range())
237+
.with_config(ariadne::Config::new().with_index_type(ariadne::IndexType::Byte))
238+
.with_message(e.to_string())
239+
.with_label(
240+
Label::new(e.span().into_range())
241+
.with_message(e.reason().to_string())
242+
.with_color(Color::Red),
243+
)
244+
.finish()
245+
.write(Source::from(src.as_ref()), &mut buffer)
246+
.unwrap();
247+
report_strings.push(String::from_utf8_lossy(&buffer).to_string());
248+
249+
if i < errs.len() - 1 {
250+
report_strings.push(String::new());
251+
}
252+
}
253+
254+
let report = report_strings.join("\n");
255+
let summary = create_error_summary(&errs, src.as_ref());
256+
257+
return Err(PathError::SyntaxError {
258+
message: format!("summary: {}, report: {}", summary, report).into(),
225259
});
226260
}
227261

@@ -247,6 +281,54 @@ impl PathIterator {
247281
}
248282
}
249283

284+
fn create_error_summary(errs: &[chumsky::error::Rich<char>], text: &str) -> String {
285+
if errs.is_empty() {
286+
return String::new();
287+
}
288+
289+
let mut summary = String::new();
290+
291+
if errs.len() == 1 {
292+
let err = &errs[0];
293+
// 计算行号和列号
294+
let (line, col) = calculate_line_col(err.span().start, text);
295+
summary.push_str(&format!(" at line {}:{} - {}", line, col, err.reason()));
296+
} else {
297+
summary.push_str(&format!(" ({} errors found):", errs.len()));
298+
for (i, err) in errs.iter().enumerate() {
299+
let (line, col) = calculate_line_col(err.span().start, text);
300+
summary.push_str(&format!(
301+
"\n {}. Line {}:{} - {}",
302+
i + 1,
303+
line,
304+
col,
305+
err.reason()
306+
));
307+
}
308+
}
309+
310+
summary
311+
}
312+
313+
fn calculate_line_col(pos: usize, text: &str) -> (usize, usize) {
314+
let mut line = 1;
315+
let mut col = 1;
316+
317+
for (i, ch) in text.char_indices() {
318+
if i >= pos {
319+
break;
320+
}
321+
if ch == '\n' {
322+
line += 1;
323+
col = 1;
324+
} else {
325+
col += 1;
326+
}
327+
}
328+
329+
(line, col)
330+
}
331+
250332
#[cfg(test)]
251333
mod tests {
252334
use super::*;

0 commit comments

Comments
 (0)