1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//! A finding a conversion or a projection reports about the text it reads or writes: its rule,
//! the file and 1-based line it concerns where it has one, the key of the node it concerns where
//! there is one, and a detail such as a count. Every such finding has the severity info; none
//! stops the conversion or the projection.
use std::fmt;
use std::path::{Component, Path, PathBuf};
use crate::identity::Key;
use crate::lint::Severity;
/// A rule an [`InfoFinding`] follows: its code and what a finding of it says.
pub trait InfoRule: Copy {
/// The rule's code, such as `OS-001`.
fn code(self) -> &'static str;
/// What a finding of the rule says, before its detail.
fn message(self) -> &'static str;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InfoFinding<R> {
pub rule: R,
pub file: Option<String>,
pub line: Option<usize>,
pub key: Option<Key>,
pub detail: Option<String>,
}
impl<R> InfoFinding<R> {
/// A finding about the line `line` of `file`.
pub const fn at(rule: R, file: String, line: usize, key: Option<Key>) -> Self {
Self {
rule,
file: Some(file),
line: Some(line),
key,
detail: None,
}
}
/// A finding about no file, with a detail.
pub const fn about(rule: R, detail: String) -> Self {
Self {
rule,
file: None,
line: None,
key: None,
detail: Some(detail),
}
}
/// The finding with its file named by its path under `dir`, without `.` components.
#[must_use]
pub fn under(self, dir: &Path) -> Self {
let file = self.file.map(|file| {
let path: PathBuf = dir
.join(file)
.components()
.filter(|part| *part != Component::CurDir)
.collect();
path.to_string_lossy().into_owned()
});
Self { file, ..self }
}
/// The finding with `detail`.
#[must_use]
pub fn with_detail(self, detail: Option<String>) -> Self {
Self { detail, ..self }
}
}
impl<R: InfoRule> fmt::Display for InfoFinding<R> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(file) = &self.file {
write!(formatter, "{file}:")?;
if let Some(line) = self.line {
write!(formatter, "{line}:")?;
}
formatter.write_str(" ")?;
}
write!(
formatter,
"{} {}: {}",
Severity::Info.as_str(),
self.rule.code(),
self.rule.message()
)?;
if let Some(detail) = &self.detail {
write!(formatter, ": {detail}")?;
}
Ok(())
}
}