fix(json): proper escapes for JSON strings (#101)

Includes two fixes:
1. Things like `\n` are not doubly-escaped any more
2. The backslash `\` itself in the source is escaped
This commit is contained in:
Paul Colomiets 2022-01-08 03:35:24 +02:00 committed by GitHub
parent 52e5ec8064
commit 645ef6a1b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 21 deletions

View File

@ -1,4 +1,4 @@
use std::fmt;
use std::fmt::{self, Write};
use crate::{protocol::Diagnostic, ReportHandler, Severity};
@ -23,20 +23,34 @@ impl Default for JSONReportHandler {
}
}
fn escape(input: &str) -> String {
input
.chars()
.map(|c| match c {
'"' => "\\\\\"".to_string(),
'\'' => "\\\\'".to_string(),
'\r' => "\\\\r".to_string(),
'\n' => "\\\\n".to_string(),
'\t' => "\\\\t".to_string(),
'\u{08}' => "\\\\b".to_string(),
'\u{0c}' => "\\\\f".to_string(),
c => format!("{}", c),
})
.collect()
struct Escape<'a>(&'a str);
impl fmt::Display for Escape<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for c in self.0.chars() {
let escape = match c {
'\\' => Some(r"\\"),
'"' => Some(r#"\""#),
'\'' => Some(r"\'"),
'\r' => Some(r"\r"),
'\n' => Some(r"\n"),
'\t' => Some(r"\t"),
'\u{08}' => Some(r"\b"),
'\u{0c}' => Some(r"\f"),
_ => None,
};
if let Some(escape) = escape {
f.write_str(escape)?;
} else {
f.write_char(c)?;
}
}
Ok(())
}
}
fn escape(input: &'_ str) -> Escape<'_> {
Escape(input)
}
impl JSONReportHandler {
@ -93,7 +107,13 @@ impl JSONReportHandler {
}
if let Some(relateds) = diagnostic.related() {
write!(f, r#""related": ["#)?;
let mut add_comma = false;
for related in relateds {
if add_comma {
write!(f, ",")?;
} else {
add_comma = true;
}
self.render_report(f, related)?;
}
write!(f, "]")?;
@ -127,3 +147,9 @@ impl ReportHandler for JSONReportHandler {
self.render_report(f, diagnostic)
}
}
#[test]
fn test_escape() {
assert_eq!(escape("a\nb").to_string(), r"a\nb");
assert_eq!(escape("C:\\Miette").to_string(), r"C:\\Miette");
}

View File

@ -497,7 +497,7 @@ mod json_report_handler {
println!("Error: {}", out);
let expected: String = r#"
{
"message": "wtf?!\\nit broke :(",
"message": "wtf?!\nit broke :(",
"code": "oops::my::bad",
"severity": "error",
"help": "try doing it better next time?",
@ -737,11 +737,18 @@ mod json_report_handler {
let err = MyBad {
src: NamedSource::new("bad_file.rs", src.clone()),
highlight: (9, 4).into(),
related: vec![MyBad {
src: NamedSource::new("bad_file2.rs", src),
highlight: (0, 6).into(),
related: vec![],
}],
related: vec![
MyBad {
src: NamedSource::new("bad_file2.rs", src.clone()),
highlight: (0, 6).into(),
related: vec![],
},
MyBad {
src: NamedSource::new("bad_file3.rs", src),
highlight: (0, 6).into(),
related: vec![],
},
],
};
let out = fmt_report(err.into());
println!("Error: {}", out);
@ -777,6 +784,22 @@ mod json_report_handler {
}
],
"related": []
},{
"message": "oops!",
"code": "oops::my::bad",
"severity": "error",
"help": "try doing it better next time?",
"filename": "bad_file3.rs",
"labels": [
{
"label": "this bit here",
"span": {
"offset": 0,
"length": 6
}
}
],
"related": []
}]
}"#
.lines()