stern4rust/rules/
header_rule.rs1use crate::offence::Offence;
6use crate::rule::Rule;
7use crate::source_file::SourceFile;
8
9pub struct HeaderRule {
20 expected: Vec<String>,
21}
22
23impl HeaderRule {
24 pub fn new(expected: Vec<String>) -> Self {
25 Self { expected }
26 }
27
28 pub fn expected(&self) -> &[String] {
29 &self.expected
30 }
31
32 fn correction(&self) -> String {
38 format!(
39 "make the first {} lines of the file match the expected header",
40 self.expected.len()
41 )
42 }
43
44 fn expected_text(&self) -> String {
45 self.expected.join("\n")
46 }
47
48 fn missing_entirely(&self, file: &SourceFile) -> Offence {
49 Offence::new(
50 file.relative_path(),
51 1,
52 self.name(),
53 "file is empty, so it carries no header".to_string(),
54 self.correction(),
55 )
56 .with_expected(&self.expected_text())
57 }
58
59 fn ends_early(&self, file: &SourceFile) -> Offence {
60 Offence::new(
61 file.relative_path(),
62 file.lines().len(),
63 self.name(),
64 format!(
65 "file has {} lines but the header is {}",
66 file.lines().len(),
67 self.expected.len()
68 ),
69 self.correction(),
70 )
71 .with_expected(&self.expected_text())
72 }
73
74 fn line_differs(&self, file: &SourceFile, index: usize) -> Offence {
75 Offence::new(
76 file.relative_path(),
77 index + 1,
78 self.name(),
79 format!(
80 "expected {:?} but found {:?}",
81 self.expected[index],
82 file.lines()[index]
83 ),
84 self.correction(),
85 )
86 .with_expected(&self.expected_text())
87 }
88}
89
90impl Rule for HeaderRule {
91 fn name(&self) -> &'static str {
92 "header"
93 }
94
95 fn check(&self, file: &SourceFile) -> Vec<Offence> {
101 if self.expected.is_empty() {
102 return Vec::new();
103 }
104 if file.is_empty() {
105 return vec![self.missing_entirely(file)];
106 }
107 let overlap = self.expected.len().min(file.lines().len());
108 if let Some(index) =
109 (0..overlap).find(|index| file.lines()[*index] != self.expected[*index])
110 {
111 return vec![self.line_differs(file, index)];
112 }
113 if file.lines().len() < self.expected.len() {
114 return vec![self.ends_early(file)];
115 }
116 Vec::new()
117 }
118}