Skip to content

Commit 6468692

Browse files
committed
Work around erased-serde round-tripping through de::Error::custom
1 parent fa8e2b1 commit 6468692

File tree

1 file changed

+65
-14
lines changed

1 file changed

+65
-14
lines changed

src/error.rs

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::error;
44
use std::fmt::{self, Debug, Display};
55
use std::io;
6+
use std::str::FromStr;
67
use std::result;
78

89
use serde::de;
@@ -388,13 +389,7 @@ impl Debug for Error {
388389
impl de::Error for Error {
389390
#[cold]
390391
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())
398393
}
399394

400395
#[cold]
@@ -410,12 +405,68 @@ impl de::Error for Error {
410405
impl ser::Error for Error {
411406
#[cold]
412407
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',
420471
}
421472
}

0 commit comments

Comments
 (0)