1use core::fmt::Display;
14
15use alloc::{borrow::Cow, vec::Vec};
16
17use crate::{Span, Spanned};
18
19#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
21pub enum Level {
22 Warning,
23 Error,
24}
25
26#[derive(Clone, PartialEq, Eq, Debug)]
27pub struct Fragment<'a> {
28 pub message: Cow<'static, str>,
30 pub span: Span,
32 pub sql_segment: &'a str,
34}
35
36#[derive(Clone, PartialEq, Eq, Debug)]
38pub struct Issue<'a> {
39 pub level: Level,
41 pub message: Cow<'static, str>,
43 pub span: Span,
45 pub sql_segment: &'a str,
47 pub fragments: Vec<Fragment<'a>>,
49}
50pub struct IssueHandle<'a, 'b> {
51 src: &'a str,
52 issue: &'b mut Issue<'a>,
53}
54
55impl<'a, 'b> IssueHandle<'a, 'b> {
56 pub fn frag(
57 &mut self,
58 message: impl Into<Cow<'static, str>>,
59 span: &impl Spanned,
60 ) -> &mut Self {
61 let span: core::ops::Range<usize> = span.span();
62 self.issue.fragments.push(Fragment {
63 message: message.into(),
64 span: span.clone(),
65 sql_segment: &self.src[span.start..span.end],
66 });
67 self
68 }
69}
70
71#[derive(Debug)]
72pub struct Issues<'a> {
73 pub src: &'a str,
74 pub issues: Vec<Issue<'a>>,
75}
76
77impl<'a> Issues<'a> {
78 pub fn err<'b>(
79 &'b mut self,
80 message: impl Into<Cow<'static, str>>,
81 span: &impl Spanned,
82 ) -> IssueHandle<'a, 'b> {
83 let span: core::ops::Range<usize> = span.span();
84 self.issues.push(Issue {
85 level: Level::Error,
86 message: message.into(),
87 span: span.clone(),
88 sql_segment: self.segment(span),
89 fragments: Default::default(),
90 });
91 IssueHandle {
92 src: self.src,
93 issue: self.issues.last_mut().unwrap(),
94 }
95 }
96
97 pub fn warn<'b>(
98 &'b mut self,
99 message: impl Into<Cow<'static, str>>,
100 span: &impl Spanned,
101 ) -> IssueHandle<'a, 'b> {
102 let span = span.span();
103 self.issues.push(Issue {
104 level: Level::Warning,
105 message: message.into(),
106 span: span.clone(),
107 sql_segment: self.segment(span),
108 fragments: Default::default(),
109 });
110 IssueHandle {
111 src: self.src,
112 issue: self.issues.last_mut().unwrap(),
113 }
114 }
115
116 pub fn segment(&self, span: Span) -> &'a str {
117 &self.src[span.start..span.end]
118 }
119
120 pub fn new(src: &'a str) -> Self {
121 Self {
122 src,
123 issues: Default::default(),
124 }
125 }
126
127 pub fn get(&self) -> &'_ [Issue<'a>] {
128 &self.issues
129 }
130
131 pub fn into_vec(self) -> Vec<Issue<'a>> {
132 self.issues
133 }
134
135 pub fn is_ok(&self) -> bool {
136 self.issues.is_empty()
137 }
138}
139
140impl<'a> Display for Issues<'a> {
141 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142 if self.issues.is_empty() {
143 return writeln!(f, "No issues");
144 }
145 writeln!(f, "Issues:")?;
146 for issue in self.get() {
147 match issue.level {
148 Level::Warning => write!(f, " Warning ")?,
149 Level::Error => write!(f, " Error ")?,
150 }
151 writeln!(f, "{}", issue.message)?;
152 let line = self.src[..issue.span.start]
153 .chars()
154 .filter(|v| *v == '\n')
155 .count()
156 + 1;
157 writeln!(f, " At line {}, code {}", line, issue.sql_segment)?;
158 for fragment in &issue.fragments {
159 writeln!(f, " {}", fragment.message)?;
160 let line = self.src[..fragment.span.start]
161 .chars()
162 .filter(|v| *v == '\n')
163 .count()
164 + 1;
165 writeln!(f, " At line {}, code {}", line, fragment.sql_segment)?;
166 }
167 }
168 Ok(())
169 }
170}