1use spirv_tools_sys::{diagnostics, shared};
2
3pub use diagnostics::MessageLevel;
4pub use shared::SpirvResult;
5
6#[derive(Debug, PartialEq)]
7pub struct Error {
8 pub inner: shared::SpirvResult,
9 pub diagnostic: Option<Diagnostic>,
10}
11
12use std::fmt;
13
14impl fmt::Display for Error {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match &self.diagnostic {
17 Some(diag) => {
18 f.write_fmt(format_args!(
19 "error:{}:{} - {}",
20 diag.line, diag.column, diag.message
21 ))?;
22
23 if !diag.notes.is_empty() {
24 f.write_fmt(format_args!("\n{}", diag.notes))?;
25 }
26
27 Ok(())
28 }
29 None => f.write_str("an unknown error occurred"),
30 }
31 }
32}
33
34impl std::error::Error for Error {
35 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
36 Some(&self.inner)
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Diagnostic {
42 pub line: usize,
43 pub column: usize,
44 pub index: usize,
45 pub message: String,
46 pub notes: String,
47 pub is_text: bool,
48}
49
50#[cfg(feature = "use-compiled-tools")]
51impl Diagnostic {
52 pub(crate) unsafe fn from_diag(
53 diag: *mut diagnostics::Diagnostic,
54 ) -> Result<Self, shared::SpirvResult> {
55 unsafe {
56 if diag.is_null() {
57 return Err(shared::SpirvResult::Success);
58 }
59
60 let (message, notes) = Message::message_and_notes_from_cstr((*diag).error);
61
62 let res = Self {
63 line: (*diag).position.line,
64 column: (*diag).position.column,
65 index: (*diag).position.index,
66 message,
67 notes,
68 is_text: (*diag).is_text_source,
69 };
70
71 diagnostics::diagnostic_destroy(diag);
72 Ok(res)
73 }
74 }
75}
76
77impl From<String> for Diagnostic {
78 fn from(message: String) -> Self {
79 Self {
80 line: 0,
81 column: 0,
82 index: 0,
83 is_text: false,
84 message,
85 notes: String::new(),
86 }
87 }
88}
89
90impl From<Message> for Diagnostic {
91 fn from(msg: Message) -> Self {
92 Self {
93 line: msg.line,
94 column: msg.column,
95 index: msg.index,
96 message: msg.message,
97 notes: msg.notes,
98 is_text: false,
99 }
100 }
101}
102
103#[derive(Debug)]
104pub struct Message {
105 pub level: MessageLevel,
106 pub source: Option<String>,
107 pub line: usize,
108 pub column: usize,
109 pub index: usize,
110 pub message: String,
111 pub notes: String,
113}
114
115impl Message {
116 #[cfg(feature = "use-installed-tools")]
117 pub(crate) fn fatal(message: String) -> Self {
118 Self {
119 level: MessageLevel::Fatal,
120 source: None,
121 line: 0,
122 column: 0,
123 index: 0,
124 message,
125 notes: String::new(),
126 }
127 }
128
129 #[cfg(feature = "use-compiled-tools")]
130 unsafe fn message_and_notes_from_cstr(msg: *const std::os::raw::c_char) -> (String, String) {
131 unsafe {
132 let full_message = std::ffi::CStr::from_ptr(msg).to_string_lossy();
133
134 if let Some(ind) = full_message.find('\n') {
135 (
136 full_message[..ind].to_owned(),
137 full_message[ind + 1..].to_owned(),
138 )
139 } else {
140 (full_message.into_owned(), String::new())
141 }
142 }
143 }
144
145 #[cfg(feature = "use-compiled-tools")]
146 pub(crate) fn from_parts(
147 level: MessageLevel,
148 source: *const std::os::raw::c_char,
149 source_pos: *const diagnostics::Position,
150 msg: *const std::os::raw::c_char,
151 ) -> Self {
152 unsafe {
153 let source = if source.is_null() {
154 None
155 } else {
156 Some(std::ffi::CStr::from_ptr(source).to_string_lossy())
157 };
158
159 let (message, notes) = Self::message_and_notes_from_cstr(msg);
160
161 let (line, column, index) = if source_pos.is_null() {
162 (0, 0, 0)
163 } else {
164 (
165 (*source_pos).line,
166 (*source_pos).column,
167 (*source_pos).index,
168 )
169 };
170
171 Self {
172 level,
173 source: source.and_then(|source| {
174 if source.is_empty() {
175 None
176 } else {
177 Some(source.into_owned())
178 }
179 }),
180 line,
181 column,
182 index,
183 message,
184 notes,
185 }
186 }
187 }
188
189 #[cfg(feature = "use-installed-tools")]
190 pub(crate) fn parse(s: &str) -> Option<Self> {
191 s.find(": ")
192 .and_then(|i| {
193 let level = match &s[..i] {
194 "error" => MessageLevel::Error,
195 "warning" => MessageLevel::Warning,
196 "info" => MessageLevel::Info,
197 _ => return None,
198 };
199
200 Some((level, i))
201 })
202 .and_then(|(level, i)| {
203 s[i + 7..]
204 .find(": ")
205 .and_then(|i2| {
206 s[i + 7..i + 7 + i2]
207 .parse::<usize>()
208 .ok()
209 .map(|index| (index, i2))
210 })
211 .map(|(index, i2)| (level, index, i + 7 + i2 + 2))
212 })
213 .map(|(level, index, last)| Self {
214 level,
215 index,
216 message: s[last..].to_owned(),
217 source: None,
218 line: 0,
219 column: 0,
220 notes: String::new(),
221 })
222 }
223}
224
225pub trait MessageCallback {
226 fn on_message(&mut self, msg: Message);
227}
228
229impl<F> MessageCallback for F
230where
231 F: FnMut(Message),
232{
233 fn on_message(&mut self, msg: Message) {
234 self(msg);
235 }
236}