Skip to main content

zyn_core/
diagnostic.rs

1pub use proc_macro2_diagnostics::Diagnostic;
2pub use proc_macro2_diagnostics::Level;
3pub use proc_macro2_diagnostics::SpanDiagnosticExt;
4
5pub type Result<T> = std::result::Result<T, Diagnostics>;
6
7#[derive(Debug)]
8pub struct Diagnostics(Vec<Diagnostic>);
9
10impl Diagnostics {
11    pub fn new() -> Self {
12        Self(Vec::new())
13    }
14
15    pub fn error(span: proc_macro2::Span, msg: impl Into<String>) -> Self {
16        Self(vec![Diagnostic::spanned(span, Level::Error, msg.into())])
17    }
18
19    pub fn push(&mut self, diag: Diagnostic) {
20        self.0.push(diag);
21    }
22
23    pub fn extend(&mut self, other: Diagnostics) {
24        self.0.extend(other.0);
25    }
26
27    pub fn is_empty(&self) -> bool {
28        self.0.is_empty()
29    }
30
31    pub fn len(&self) -> usize {
32        self.0.len()
33    }
34
35    pub fn has_errors(&self) -> bool {
36        self.0.iter().any(|d| d.level() == Level::Error)
37    }
38
39    pub fn max_level(&self) -> Option<Level> {
40        self.0
41            .iter()
42            .map(|d| d.level())
43            .max_by_key(|l| level_ord(*l))
44    }
45
46    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
47        self.0.iter()
48    }
49
50    pub fn emit(self) -> proc_macro2::TokenStream {
51        let mut tokens = proc_macro2::TokenStream::new();
52
53        for diag in self.0 {
54            tokens.extend(diag.emit_as_item_tokens());
55        }
56
57        tokens
58    }
59}
60
61fn level_ord(level: Level) -> u8 {
62    match level {
63        Level::Note => 0,
64        Level::Help => 1,
65        Level::Warning => 2,
66        Level::Error => 3,
67        _ => 4,
68    }
69}
70
71impl std::fmt::Display for Diagnostics {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        for (i, diag) in self.0.iter().enumerate() {
74            if i > 0 {
75                writeln!(f)?;
76            }
77
78            write!(f, "{:?}", diag)?;
79        }
80
81        Ok(())
82    }
83}
84
85impl Default for Diagnostics {
86    fn default() -> Self {
87        Self::new()
88    }
89}
90
91impl From<Diagnostic> for Diagnostics {
92    fn from(diag: Diagnostic) -> Self {
93        Self(vec![diag])
94    }
95}
96
97impl From<syn::Error> for Diagnostics {
98    fn from(error: syn::Error) -> Self {
99        let diags = error
100            .into_iter()
101            .map(|e| Diagnostic::spanned(e.span(), Level::Error, e.to_string()))
102            .collect();
103
104        Self(diags)
105    }
106}
107
108impl IntoIterator for Diagnostics {
109    type Item = Diagnostic;
110    type IntoIter = std::vec::IntoIter<Diagnostic>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.0.into_iter()
114    }
115}
116
117impl<'a> IntoIterator for &'a Diagnostics {
118    type Item = &'a Diagnostic;
119    type IntoIter = std::slice::Iter<'a, Diagnostic>;
120
121    fn into_iter(self) -> Self::IntoIter {
122        self.0.iter()
123    }
124}
125
126pub trait ToDiagnostics {
127    fn to_diagnostics(self) -> Diagnostics;
128}
129
130impl ToDiagnostics for syn::Error {
131    fn to_diagnostics(self) -> Diagnostics {
132        Diagnostics::from(self)
133    }
134}
135
136impl ToDiagnostics for Diagnostics {
137    fn to_diagnostics(self) -> Diagnostics {
138        self
139    }
140}