oo_ide/log_matcher/
message.rs1use std::fmt;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct Reference {
18 pub filename: String,
20 pub line: Option<u32>,
21 pub column: Option<u32>,
22}
23
24impl Reference {
25 pub fn new(filename: impl Into<String>) -> Self {
26 Self {
27 filename: filename.into(),
28 line: None,
29 column: None,
30 }
31 }
32}
33
34impl fmt::Display for Reference {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "{}", self.filename)?;
37 if let Some(l) = self.line {
38 write!(f, ":{}", l)?;
39 if let Some(c) = self.column {
40 write!(f, ":{}", c)?;
41 }
42 }
43 Ok(())
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct AnnotatedRef {
54 pub reference: Reference,
55 pub label: String,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
64pub enum MessageLevel {
65 Info,
66 Warning,
67 Error,
68}
69
70impl fmt::Display for MessageLevel {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 match self {
73 Self::Error => write!(f, "error"),
74 Self::Warning => write!(f, "warning"),
75 Self::Info => write!(f, "info"),
76 }
77 }
78}
79
80#[derive(Debug, Clone)]
92pub struct Message {
93 pub level: MessageLevel,
94 pub text: String,
95 pub reference: Option<Reference>,
97 pub related: Vec<AnnotatedRef>,
99}
100
101impl Message {
102 pub fn error(text: impl Into<String>) -> Self {
103 Self {
104 level: MessageLevel::Error,
105 text: text.into(),
106 reference: None,
107 related: vec![],
108 }
109 }
110
111 pub fn error_at(text: impl Into<String>, reference: impl Into<String>) -> Self {
112 Self {
113 level: MessageLevel::Error,
114 text: text.into(),
115 reference: Some(Reference::new(reference)),
116 related: vec![],
117 }
118 }
119
120 pub fn warning_at(text: impl Into<String>, reference: impl Into<String>) -> Self {
121 Self {
122 level: MessageLevel::Warning,
123 text: text.into(),
124 reference: Some(Reference::new(reference)),
125 related: vec![],
126 }
127 }
128
129 pub fn with_related(mut self, reference: impl Into<String>, label: impl Into<String>) -> Self {
131 self.related.push(AnnotatedRef {
132 reference: Reference::new(reference),
133 label: label.into(),
134 });
135 self
136 }
137}
138
139impl fmt::Display for Message {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(f, "{}: {}", self.level, self.text)?;
142 if let Some(r) = &self.reference {
143 write!(f, "\n --> {}", r)?;
144 }
145 for ann in &self.related {
146 write!(f, "\n = note: {}: {}", ann.label, ann.reference)?;
147 }
148 Ok(())
149 }
150}
151
152pub fn has_errors(msgs: &[Message]) -> bool {
158 msgs.iter().any(|m| m.level == MessageLevel::Error)
159}
160
161#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn message_display_error_with_reference() {
171 let msg = Message::error_at("something went wrong", "file.yaml:matchers[0].id");
172 let s = msg.to_string();
173 assert!(s.contains("error: something went wrong"));
174 assert!(s.contains("file.yaml:matchers[0].id"));
175 }
176
177 #[test]
178 fn message_display_with_related() {
179 let msg = Message::error_at("duplicate id: foo", "file.yaml:matchers[1].id")
180 .with_related("file.yaml:matchers[0].id", "first defined here");
181 let s = msg.to_string();
182 assert!(s.contains("first defined here"));
183 assert!(s.contains("file.yaml:matchers[0].id"));
184 }
185
186 #[test]
187 fn has_errors_detects_error_level() {
188 let msgs = vec![
189 Message::warning_at("a warning", "somewhere"),
190 Message::error_at("an error", "somewhere"),
191 ];
192 assert!(has_errors(&msgs));
193 }
194
195 #[test]
196 fn has_errors_false_for_warnings_only() {
197 let msgs = vec![Message::warning_at("just a warning", "somewhere")];
198 assert!(!has_errors(&msgs));
199 }
200
201 #[test]
202 fn has_errors_false_for_empty() {
203 assert!(!has_errors(&[]));
204 }
205}