xtask/tasks/fmt/lints/
trailing_newline.rs1use super::Lint;
7use super::LintCtx;
8use super::Lintable;
9use toml_edit::DocumentMut;
10
11const CHECKED_EXTENSIONS: &[&str] = &[
12 "c", "md", "proto", "py", "rs", "sh", "toml", "txt", "yml", "js", "ts",
13];
14
15pub struct TrailingNewline;
16
17impl Lint for TrailingNewline {
18 fn new(_ctx: &LintCtx) -> Self {
19 TrailingNewline
20 }
21
22 fn enter_workspace(&mut self, _content: &Lintable<DocumentMut>) {}
23 fn enter_crate(&mut self, _content: &Lintable<DocumentMut>) {}
24
25 fn visit_file(&mut self, content: &mut Lintable<String>) {
26 let mut ending_newlines = 0;
27 for c in content.as_bytes().iter().rev() {
28 if *c == b'\n' {
29 ending_newlines += 1;
30 } else {
31 break;
32 }
33 }
34
35 if ending_newlines == 0 {
36 content.fix("missing trailing newline", |content| content.push('\n'));
37 } else if ending_newlines != 1 {
38 content.fix("too many trailing newlines", |content| {
39 content.truncate(content.len() - ending_newlines + 1);
41 });
42 }
43 }
44
45 fn exit_crate(&mut self, content: &mut Lintable<DocumentMut>) {
46 let mut ending_newlines = 0;
50 for c in content.raw().unwrap().as_bytes().iter().rev() {
51 if *c == b'\n' {
52 ending_newlines += 1;
53 } else {
54 break;
55 }
56 }
57
58 if ending_newlines != 1 {
63 content.fix("missing or too many trailing newlines", |content| {
64 content.set_trailing(content.trailing().as_str().unwrap().trim_end().to_owned());
65 });
66 }
67 }
68
69 fn exit_workspace(&mut self, content: &mut Lintable<DocumentMut>) {
70 self.exit_crate(content)
71 }
72
73 fn visit_nonrust_file(&mut self, extension: &str, content: &mut Lintable<String>) {
74 if content.path().file_name().unwrap_or_default() == "toc.yml" {
76 return;
77 }
78
79 if CHECKED_EXTENSIONS.contains(&extension) {
81 self.visit_file(content)
82 }
83 }
84}