11use std:: { fmt, str} ;
22
3+ use ariadne:: { Color , Label , Report , ReportKind , Source } ;
34use chumsky:: prelude:: * ;
45use pilota:: FastStr ;
56use pilota_thrift_parser:: descriptor:: Components ;
67use thiserror:: Error ;
78
89#[ derive( Debug , Clone , Error ) ]
910pub 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) ]
251333mod tests {
252334 use super :: * ;
0 commit comments