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