1use serde::Serialize;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
11#[serde(rename_all = "lowercase")]
12pub enum Severity {
13 Error,
14 Warning,
15}
16
17impl std::fmt::Display for Severity {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 match self {
20 Severity::Error => f.write_str("error"),
21 Severity::Warning => f.write_str("warning"),
22 }
23 }
24}
25
26#[derive(Debug, Clone, PartialEq, Serialize)]
28pub struct Diagnostic {
29 pub severity: Severity,
30 pub path: String,
31 pub message: String,
32}
33
34impl std::fmt::Display for Diagnostic {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "{}: {}: {}", self.severity, self.path, self.message)
37 }
38}
39
40#[derive(Debug, Clone, Default, PartialEq, Serialize)]
45pub struct ParseReport {
46 errors: Vec<Diagnostic>,
47 warnings: Vec<Diagnostic>,
48}
49
50impl ParseReport {
51 pub(crate) fn error(&mut self, path: impl Into<String>, message: impl Into<String>) {
53 self.errors.push(Diagnostic {
54 severity: Severity::Error,
55 path: path.into(),
56 message: message.into(),
57 });
58 }
59
60 pub(crate) fn warn(&mut self, path: impl Into<String>, message: impl Into<String>) {
62 self.warnings.push(Diagnostic {
63 severity: Severity::Warning,
64 path: path.into(),
65 message: message.into(),
66 });
67 }
68
69 pub fn errors(&self) -> &[Diagnostic] {
71 &self.errors
72 }
73
74 pub fn warnings(&self) -> &[Diagnostic] {
76 &self.warnings
77 }
78
79 pub fn is_ok(&self) -> bool {
81 self.errors.is_empty()
82 }
83}
84
85impl std::fmt::Display for ParseReport {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 for d in &self.errors {
88 writeln!(f, "{d}")?;
89 }
90 for d in &self.warnings {
91 writeln!(f, "{d}")?;
92 }
93 write!(
94 f,
95 "{} error(s), {} warning(s)",
96 self.errors.len(),
97 self.warnings.len()
98 )
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn ok_and_accessors() {
108 let mut r = ParseReport::default();
109 assert!(r.is_ok());
110 r.warn("document", "empty");
111 assert!(r.is_ok()); assert_eq!(r.warnings().len(), 1);
113 r.error("nodes[N01]", "duplicate node id");
114 assert!(!r.is_ok());
115 assert_eq!(r.errors().len(), 1);
116 }
117
118 #[test]
119 fn diagnostic_display() {
120 let d = Diagnostic {
121 severity: Severity::Error,
122 path: "nodes[N07].evidence[0]".into(),
123 message: "unknown claim".into(),
124 };
125 assert_eq!(
126 d.to_string(),
127 "error: nodes[N07].evidence[0]: unknown claim"
128 );
129 }
130}