haloumi_ir/
diagnostics.rs1use std::{fmt, sync::Arc};
4
5#[derive(Debug, Default)]
7pub struct Validation<D> {
8 errors: Vec<D>,
9 warnings: Vec<D>,
10}
11
12impl<D> Validation<D> {
13 pub fn new() -> Self {
15 Self {
16 errors: vec![],
17 warnings: vec![],
18 }
19 }
20
21 pub fn with_warning(&mut self, warning: D) {
23 self.warnings.push(warning)
24 }
25
26 pub fn with_error(&mut self, error: D) {
28 self.errors.push(error)
29 }
30
31 pub fn with_warnings(&mut self, warnings: impl IntoIterator<Item = D>) {
33 self.warnings.extend(warnings)
34 }
35
36 pub fn with_errors(&mut self, errors: impl IntoIterator<Item = D>) {
38 self.errors.extend(errors)
39 }
40
41 pub fn append(&mut self, diags: impl IntoIterator<Item = D>)
43 where
44 D: Diagnostic,
45 {
46 let (errors, warnings): (Vec<_>, Vec<_>) = diags.into_iter().partition(D::is_error);
47 self.with_warnings(warnings);
48 self.with_errors(errors);
49 }
50
51 pub fn append_from_result<C>(&mut self, result: Result<Vec<D>, Vec<D>>, context: &C)
53 where
54 D: Diagnostic,
55 C: std::fmt::Display + ?Sized,
56 {
57 match result {
58 Ok(warnings) => self.append(warnings.into_iter().map(|w| w.contextualize(context))),
59 Err(errors) => self.append(errors.into_iter().map(|e| e.contextualize(context))),
60 }
61 }
62}
63
64impl<D> From<Validation<D>> for Result<Vec<D>, Vec<D>> {
65 fn from(mut value: Validation<D>) -> Self {
66 if value.errors.is_empty() {
67 Ok(value.warnings)
68 } else {
69 value.errors.extend(value.warnings);
70 Err(value.errors)
71 }
72 }
73}
74
75#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
77pub enum DiagnosticKind {
78 Warning,
80 Error,
82 Other,
84}
85
86pub trait Diagnostic: fmt::Display {
88 fn kind(&self) -> DiagnosticKind;
90
91 fn is_error(&self) -> bool {
93 self.kind() == DiagnosticKind::Error
94 }
95
96 fn contextualize<C>(self, context: &C) -> Self
98 where
99 C: ToString + ?Sized,
100 Self: Sized;
101}
102
103#[derive(Debug, Clone)]
105pub struct SimpleDiagnostic {
106 kind: DiagnosticKind,
107 message: String,
108}
109
110impl SimpleDiagnostic {
111 pub fn warn(message: impl ToString) -> Self {
113 Self {
114 kind: DiagnosticKind::Warning,
115 message: message.to_string(),
116 }
117 }
118
119 pub fn error(message: impl ToString) -> Self {
121 Self {
122 kind: DiagnosticKind::Error,
123 message: message.to_string(),
124 }
125 }
126
127 pub fn other(message: impl ToString) -> Self {
129 Self {
130 kind: DiagnosticKind::Other,
131 message: message.to_string(),
132 }
133 }
134}
135
136impl Diagnostic for SimpleDiagnostic {
137 fn kind(&self) -> DiagnosticKind {
138 self.kind
139 }
140
141 fn contextualize<C>(self, context: &C) -> Self
142 where
143 C: ToString + ?Sized,
144 {
145 let mut final_message = context.to_string();
146 if final_message.is_empty() {
147 return self;
148 }
149 final_message.reserve(2 + self.message.len());
150 final_message.push_str(": ");
151 final_message.push_str(&self.message);
152 Self {
153 kind: self.kind,
154 message: final_message,
155 }
156 }
157}
158
159impl fmt::Display for SimpleDiagnostic {
160 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161 write!(
162 f,
163 "{}: {}",
164 match self.kind {
165 DiagnosticKind::Warning => "warning",
166 DiagnosticKind::Error => "error",
167 DiagnosticKind::Other => "other",
168 },
169 self.message
170 )
171 }
172}
173
174pub struct DiagnosticsError {
176 diags: Vec<Arc<dyn Diagnostic + Send + Sync + 'static>>,
177}
178
179impl std::fmt::Debug for DiagnosticsError {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 f.debug_struct("DiagnosticsError")
182 .field("n_diags", &self.diags.len())
183 .finish()
184 }
185}
186
187impl std::fmt::Display for DiagnosticsError {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 self.diags
190 .iter()
191 .map(AsRef::as_ref)
192 .try_for_each(|diag| writeln!(f, "- {diag}\n"))
193 }
194}
195
196impl<D: Diagnostic + Send + Sync + 'static> FromIterator<D> for DiagnosticsError {
197 fn from_iter<T: IntoIterator<Item = D>>(iter: T) -> Self {
198 Self {
199 diags: iter
200 .into_iter()
201 .map(|d| -> Arc<dyn Diagnostic + Send + Sync + 'static> { Arc::new(d) })
202 .collect(),
203 }
204 }
205}
206
207impl<I: IntoIterator> From<I> for DiagnosticsError
208where
209 I::Item: Diagnostic + Send + Sync + 'static,
210{
211 fn from(value: I) -> Self {
212 Self::from_iter(value)
213 }
214}