3
3
use std:: error;
4
4
use std:: fmt:: { self , Debug , Display } ;
5
5
use std:: io;
6
+ use std:: str:: FromStr ;
6
7
use std:: result;
7
8
8
9
use serde:: de;
@@ -388,13 +389,7 @@ impl Debug for Error {
388
389
impl de:: Error for Error {
389
390
#[ cold]
390
391
fn custom < T : Display > ( msg : T ) -> Error {
391
- Error {
392
- err : Box :: new ( ErrorImpl {
393
- code : ErrorCode :: Message ( msg. to_string ( ) . into_boxed_str ( ) ) ,
394
- line : 0 ,
395
- column : 0 ,
396
- } ) ,
397
- }
392
+ make_error ( msg. to_string ( ) )
398
393
}
399
394
400
395
#[ cold]
@@ -410,12 +405,68 @@ impl de::Error for Error {
410
405
impl ser:: Error for Error {
411
406
#[ cold]
412
407
fn custom < T : Display > ( msg : T ) -> Error {
413
- Error {
414
- err : Box :: new ( ErrorImpl {
415
- code : ErrorCode :: Message ( msg. to_string ( ) . into_boxed_str ( ) ) ,
416
- line : 0 ,
417
- column : 0 ,
418
- } ) ,
419
- }
408
+ make_error ( msg. to_string ( ) )
409
+ }
410
+ }
411
+
412
+ // Parse our own error message that looks like "{} at line {} column {}" to work
413
+ // around erased-serde round-tripping the error through de::Error::custom.
414
+ fn make_error ( mut msg : String ) -> Error {
415
+ let ( line, column) = parse_line_col ( & mut msg) . unwrap_or ( ( 0 , 0 ) ) ;
416
+ Error {
417
+ err : Box :: new ( ErrorImpl {
418
+ code : ErrorCode :: Message ( msg. into_boxed_str ( ) ) ,
419
+ line : line,
420
+ column : column,
421
+ } ) ,
422
+ }
423
+ }
424
+
425
+ fn parse_line_col ( msg : & mut String ) -> Option < ( usize , usize ) > {
426
+ let start_of_suffix = match msg. rfind ( " at line " ) {
427
+ Some ( index) => index,
428
+ None => return None ,
429
+ } ;
430
+
431
+ // Find start and end of line number.
432
+ let start_of_line = start_of_suffix + " at line " . len ( ) ;
433
+ let mut end_of_line = start_of_line;
434
+ while starts_with_digit ( & msg[ end_of_line..] ) {
435
+ end_of_line += 1 ;
436
+ }
437
+
438
+ if !msg[ end_of_line..] . starts_with ( " column " ) {
439
+ return None ;
440
+ }
441
+
442
+ // Find start and end of column number.
443
+ let start_of_column = end_of_line + " column " . len ( ) ;
444
+ let mut end_of_column = start_of_column;
445
+ while starts_with_digit ( & msg[ end_of_column..] ) {
446
+ end_of_column += 1 ;
447
+ }
448
+
449
+ if end_of_column < msg. len ( ) {
450
+ return None ;
451
+ }
452
+
453
+ // Parse numbers.
454
+ let line = match usize:: from_str ( & msg[ start_of_line..end_of_line] ) {
455
+ Ok ( line) => line,
456
+ Err ( _) => return None ,
457
+ } ;
458
+ let column = match usize:: from_str ( & msg[ start_of_column..end_of_column] ) {
459
+ Ok ( column) => column,
460
+ Err ( _) => return None ,
461
+ } ;
462
+
463
+ msg. truncate ( start_of_suffix) ;
464
+ Some ( ( line, column) )
465
+ }
466
+
467
+ fn starts_with_digit ( slice : & str ) -> bool {
468
+ match slice. as_bytes ( ) . get ( 0 ) {
469
+ None => false ,
470
+ Some ( & byte) => byte >= b'0' && byte <= b'9' ,
420
471
}
421
472
}
0 commit comments