mirror of https://github.com/zkat/miette.git
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:
parent
52e5ec8064
commit
645ef6a1b6
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue