Skip to content

Commit dbac28b

Browse files
Ruben Schmidmeistertopecongiro
Ruben Schmidmeister
authored andcommitted
Use trait to abstract emit modes (#3616)
1 parent 1cea171 commit dbac28b

File tree

13 files changed

+363
-179
lines changed

13 files changed

+363
-179
lines changed

src/checkstyle.rs

Lines changed: 0 additions & 102 deletions
This file was deleted.

src/emitter.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
pub(crate) use self::checkstyle::*;
2+
pub(crate) use self::diff::*;
3+
pub(crate) use self::files::*;
4+
pub(crate) use self::files_with_backup::*;
5+
pub(crate) use self::modified_lines::*;
6+
pub(crate) use self::stdout::*;
7+
use crate::FileName;
8+
use std::io::{self, Write};
9+
use std::path::Path;
10+
11+
mod checkstyle;
12+
mod diff;
13+
mod files;
14+
mod files_with_backup;
15+
mod modified_lines;
16+
mod stdout;
17+
18+
pub(crate) struct FormattedFile<'a> {
19+
pub(crate) filename: &'a FileName,
20+
pub(crate) original_text: &'a str,
21+
pub(crate) formatted_text: &'a str,
22+
}
23+
24+
#[derive(Debug, Default, Clone)]
25+
pub(crate) struct EmitterResult {
26+
pub(crate) has_diff: bool,
27+
}
28+
29+
pub(crate) trait Emitter {
30+
fn emit_formatted_file(
31+
&self,
32+
output: &mut dyn Write,
33+
formatted_file: FormattedFile<'_>,
34+
) -> Result<EmitterResult, io::Error>;
35+
36+
fn emit_header(&self, _output: &mut dyn Write) -> Result<(), io::Error> {
37+
Ok(())
38+
}
39+
40+
fn emit_footer(&self, _output: &mut dyn Write) -> Result<(), io::Error> {
41+
Ok(())
42+
}
43+
}
44+
45+
fn ensure_real_path(filename: &FileName) -> &Path {
46+
match *filename {
47+
FileName::Real(ref path) => path,
48+
_ => panic!("cannot format `{}` and emit to files", filename),
49+
}
50+
}

src/emitter/checkstyle.rs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
use self::xml::XmlEscaped;
2+
use super::*;
3+
use crate::rustfmt_diff::{make_diff, DiffLine, Mismatch};
4+
use std::io::{self, Write};
5+
use std::path::Path;
6+
7+
mod xml;
8+
9+
#[derive(Debug, Default)]
10+
pub(crate) struct CheckstyleEmitter;
11+
12+
impl Emitter for CheckstyleEmitter {
13+
fn emit_header(&self, output: &mut dyn Write) -> Result<(), io::Error> {
14+
writeln!(output, r#"<?xml version="1.0" encoding="utf-8"?>"#)?;
15+
write!(output, r#"<checkstyle version="4.3">"#)?;
16+
Ok(())
17+
}
18+
19+
fn emit_footer(&self, output: &mut dyn Write) -> Result<(), io::Error> {
20+
writeln!(output, "</checkstyle>")
21+
}
22+
23+
fn emit_formatted_file(
24+
&self,
25+
output: &mut dyn Write,
26+
FormattedFile {
27+
filename,
28+
original_text,
29+
formatted_text,
30+
}: FormattedFile<'_>,
31+
) -> Result<EmitterResult, io::Error> {
32+
const CONTEXT_SIZE: usize = 3;
33+
let filename = ensure_real_path(filename);
34+
let diff = make_diff(original_text, formatted_text, CONTEXT_SIZE);
35+
output_checkstyle_file(output, filename, diff)?;
36+
Ok(EmitterResult::default())
37+
}
38+
}
39+
40+
pub(crate) fn output_checkstyle_file<T>(
41+
mut writer: T,
42+
filename: &Path,
43+
diff: Vec<Mismatch>,
44+
) -> Result<(), io::Error>
45+
where
46+
T: Write,
47+
{
48+
write!(writer, r#"<file name="{}">"#, filename.display())?;
49+
for mismatch in diff {
50+
for line in mismatch.lines {
51+
// Do nothing with `DiffLine::Context` and `DiffLine::Resulting`.
52+
if let DiffLine::Expected(message) = line {
53+
write!(
54+
writer,
55+
r#"<error line="{}" severity="warning" message="Should be `{}`" />"#,
56+
mismatch.line_number,
57+
XmlEscaped(&message)
58+
)?;
59+
}
60+
}
61+
}
62+
write!(writer, "</file>")?;
63+
Ok(())
64+
}

src/emitter/checkstyle/xml.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use std::fmt::{self, Display};
2+
3+
/// Convert special characters into XML entities.
4+
/// This is needed for checkstyle output.
5+
pub(super) struct XmlEscaped<'a>(pub(super) &'a str);
6+
7+
impl<'a> Display for XmlEscaped<'a> {
8+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
9+
for char in self.0.chars() {
10+
match char {
11+
'<' => write!(formatter, "&lt;"),
12+
'>' => write!(formatter, "&gt;"),
13+
'"' => write!(formatter, "&quot;"),
14+
'\'' => write!(formatter, "&apos;"),
15+
'&' => write!(formatter, "&amp;"),
16+
_ => write!(formatter, "{}", char),
17+
}?;
18+
}
19+
20+
Ok(())
21+
}
22+
}
23+
24+
#[cfg(test)]
25+
mod tests {
26+
use super::*;
27+
28+
#[test]
29+
fn special_characters_are_escaped() {
30+
assert_eq!(
31+
"&lt;&gt;&quot;&apos;&amp;",
32+
format!("{}", XmlEscaped(r#"<>"'&"#)),
33+
);
34+
}
35+
36+
#[test]
37+
fn special_characters_are_escaped_in_string_with_other_characters() {
38+
assert_eq!(
39+
"The quick brown &quot;🦊&quot; jumps &lt;over&gt; the lazy 🐶",
40+
format!(
41+
"{}",
42+
XmlEscaped(r#"The quick brown "🦊" jumps <over> the lazy 🐶"#)
43+
),
44+
);
45+
}
46+
47+
#[test]
48+
fn other_characters_are_not_escaped() {
49+
let string = "The quick brown 🦊 jumps over the lazy 🐶";
50+
assert_eq!(string, format!("{}", XmlEscaped(string)));
51+
}
52+
}

src/emitter/diff.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use super::*;
2+
use crate::config::Config;
3+
use crate::rustfmt_diff::{make_diff, print_diff};
4+
5+
pub(crate) struct DiffEmitter {
6+
config: Config,
7+
}
8+
9+
impl DiffEmitter {
10+
pub(crate) fn new(config: Config) -> Self {
11+
Self { config }
12+
}
13+
}
14+
15+
impl Emitter for DiffEmitter {
16+
fn emit_formatted_file(
17+
&self,
18+
_output: &mut dyn Write,
19+
FormattedFile {
20+
filename,
21+
original_text,
22+
formatted_text,
23+
}: FormattedFile<'_>,
24+
) -> Result<EmitterResult, io::Error> {
25+
const CONTEXT_SIZE: usize = 3;
26+
let mismatch = make_diff(&original_text, formatted_text, CONTEXT_SIZE);
27+
let has_diff = !mismatch.is_empty();
28+
print_diff(
29+
mismatch,
30+
|line_num| format!("Diff in {} at line {}:", filename, line_num),
31+
&self.config,
32+
);
33+
return Ok(EmitterResult { has_diff });
34+
}
35+
}

src/emitter/files.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use super::*;
2+
use std::fs;
3+
4+
#[derive(Debug, Default)]
5+
pub(crate) struct FilesEmitter;
6+
7+
impl Emitter for FilesEmitter {
8+
fn emit_formatted_file(
9+
&self,
10+
_output: &mut dyn Write,
11+
FormattedFile {
12+
filename,
13+
original_text,
14+
formatted_text,
15+
}: FormattedFile<'_>,
16+
) -> Result<EmitterResult, io::Error> {
17+
// Write text directly over original file if there is a diff.
18+
let filename = ensure_real_path(filename);
19+
if original_text != formatted_text {
20+
fs::write(filename, formatted_text)?;
21+
}
22+
Ok(EmitterResult::default())
23+
}
24+
}

src/emitter/files_with_backup.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use super::*;
2+
use std::fs;
3+
4+
#[derive(Debug, Default)]
5+
pub(crate) struct FilesWithBackupEmitter;
6+
7+
impl Emitter for FilesWithBackupEmitter {
8+
fn emit_formatted_file(
9+
&self,
10+
_output: &mut dyn Write,
11+
FormattedFile {
12+
filename,
13+
original_text,
14+
formatted_text,
15+
}: FormattedFile<'_>,
16+
) -> Result<EmitterResult, io::Error> {
17+
let filename = ensure_real_path(filename);
18+
if original_text != formatted_text {
19+
// Do a little dance to make writing safer - write to a temp file
20+
// rename the original to a .bk, then rename the temp file to the
21+
// original.
22+
let tmp_name = filename.with_extension("tmp");
23+
let bk_name = filename.with_extension("bk");
24+
25+
fs::write(&tmp_name, formatted_text)?;
26+
fs::rename(filename, bk_name)?;
27+
fs::rename(tmp_name, filename)?;
28+
}
29+
Ok(EmitterResult::default())
30+
}
31+
}

src/emitter/modified_lines.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use super::*;
2+
use crate::rustfmt_diff::{make_diff, ModifiedLines};
3+
use std::io::Write;
4+
5+
#[derive(Debug, Default)]
6+
pub(crate) struct ModifiedLinesEmitter;
7+
8+
impl Emitter for ModifiedLinesEmitter {
9+
fn emit_formatted_file(
10+
&self,
11+
output: &mut dyn Write,
12+
FormattedFile {
13+
original_text,
14+
formatted_text,
15+
..
16+
}: FormattedFile<'_>,
17+
) -> Result<EmitterResult, io::Error> {
18+
const CONTEXT_SIZE: usize = 0;
19+
let mismatch = make_diff(original_text, formatted_text, CONTEXT_SIZE);
20+
let has_diff = !mismatch.is_empty();
21+
write!(output, "{}", ModifiedLines::from(mismatch))?;
22+
Ok(EmitterResult { has_diff })
23+
}
24+
}

0 commit comments

Comments
 (0)