1use std::{borrow::Cow, convert::Infallible, error, ffi::CStr, fmt, str};
7
8use crate::{ffi, translate::*, Quark};
9
10wrapper! {
11 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
17 #[doc(alias = "GError")]
18 pub struct Error(Boxed<ffi::GError>);
19
20 match fn {
21 copy => |ptr| ffi::g_error_copy(ptr),
22 free => |ptr| ffi::g_error_free(ptr),
23 type_ => || ffi::g_error_get_type(),
24 }
25}
26
27unsafe impl Send for Error {}
28unsafe impl Sync for Error {}
29
30impl Error {
31 #[doc(alias = "g_error_new_literal")]
47 #[doc(alias = "g_error_new")]
48 pub fn new<T: ErrorDomain>(error: T, message: &str) -> Error {
49 unsafe {
50 from_glib_full(ffi::g_error_new_literal(
51 T::domain().into_glib(),
52 error.code(),
53 message.to_glib_none().0,
54 ))
55 }
56 }
57
58 pub fn is<T: ErrorDomain>(&self) -> bool {
61 self.inner.domain == T::domain().into_glib()
62 }
63
64 pub fn domain(&self) -> Quark {
67 unsafe { from_glib(self.inner.domain) }
68 }
69
70 #[doc(alias = "g_error_matches")]
92 pub fn matches<T: ErrorDomain>(&self, err: T) -> bool {
93 self.is::<T>() && self.inner.code == err.code()
94 }
95
96 pub fn kind<T: ErrorDomain>(&self) -> Option<T> {
114 if self.is::<T>() {
115 T::from(self.inner.code)
116 } else {
117 None
118 }
119 }
120
121 pub fn message(&self) -> &str {
127 unsafe {
128 let bytes = CStr::from_ptr(self.inner.message).to_bytes();
129 str::from_utf8(bytes)
130 .unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap())
131 }
132 }
133}
134
135impl fmt::Display for Error {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 f.write_str(self.message())
138 }
139}
140
141impl fmt::Debug for Error {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 f.debug_struct("Error")
144 .field("domain", unsafe {
145 &crate::Quark::from_glib(self.inner.domain)
146 })
147 .field("code", &self.inner.code)
148 .field("message", &self.message())
149 .finish()
150 }
151}
152
153impl error::Error for Error {}
154
155impl From<Infallible> for Error {
156 fn from(e: Infallible) -> Self {
157 match e {}
158 }
159}
160
161pub trait ErrorDomain: Copy {
166 fn domain() -> Quark;
171
172 fn code(self) -> i32;
175
176 fn from(code: i32) -> Option<Self>
182 where
183 Self: Sized;
184}
185
186#[macro_export]
189macro_rules! bool_error(
190 ($($msg:tt)*) => {{
191 match ::std::format_args!($($msg)*) {
192 formatted => {
193 if let Some(s) = formatted.as_str() {
194 $crate::BoolError::new(
195 s,
196 file!(),
197 $crate::function_name!(),
198 line!()
199 )
200 } else {
201 $crate::BoolError::new(
202 formatted.to_string(),
203 file!(),
204 $crate::function_name!(),
205 line!(),
206 )
207 }
208 }
209 }
210 }};
211);
212
213#[macro_export]
214macro_rules! result_from_gboolean(
215 ($ffi_bool:expr, $($msg:tt)*) => {{
216 match ::std::format_args!($($msg)*) {
217 formatted => {
218 if let Some(s) = formatted.as_str() {
219 $crate::BoolError::from_glib(
220 $ffi_bool,
221 s,
222 file!(),
223 $crate::function_name!(),
224 line!(),
225 )
226 } else {
227 $crate::BoolError::from_glib(
228 $ffi_bool,
229 formatted.to_string(),
230 file!(),
231 $crate::function_name!(),
232 line!(),
233 )
234 }
235 }
236 }
237
238
239 }};
240);
241
242#[derive(Debug, Clone)]
243pub struct BoolError {
244 pub message: Cow<'static, str>,
245 #[doc(hidden)]
246 pub filename: &'static str,
247 #[doc(hidden)]
248 pub function: &'static str,
249 #[doc(hidden)]
250 pub line: u32,
251}
252
253impl BoolError {
254 pub fn new(
255 message: impl Into<Cow<'static, str>>,
256 filename: &'static str,
257 function: &'static str,
258 line: u32,
259 ) -> Self {
260 Self {
261 message: message.into(),
262 filename,
263 function,
264 line,
265 }
266 }
267
268 pub fn from_glib(
269 b: ffi::gboolean,
270 message: impl Into<Cow<'static, str>>,
271 filename: &'static str,
272 function: &'static str,
273 line: u32,
274 ) -> Result<(), Self> {
275 match b {
276 ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
277 _ => Ok(()),
278 }
279 }
280}
281
282impl fmt::Display for BoolError {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284 f.write_str(&self.message)
285 }
286}
287
288impl error::Error for BoolError {}
289
290#[cfg(test)]
291mod tests {
292 use std::ffi::CString;
293
294 use super::*;
295 use crate::prelude::*;
296
297 #[test]
298 fn test_error_matches() {
299 let e = Error::new(crate::FileError::Failed, "Failed");
300 assert!(e.matches(crate::FileError::Failed));
301 assert!(!e.matches(crate::FileError::Again));
302 assert!(!e.matches(crate::KeyFileError::NotFound));
303 }
304
305 #[test]
306 fn test_error_kind() {
307 let e = Error::new(crate::FileError::Failed, "Failed");
308 assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
309 assert_eq!(e.kind::<crate::KeyFileError>(), None);
310 }
311
312 #[test]
313 fn test_into_raw() {
314 unsafe {
315 let e: *mut ffi::GError =
316 Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
317 assert_eq!((*e).domain, ffi::g_file_error_quark());
318 assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
319 assert_eq!(
320 CStr::from_ptr((*e).message),
321 CString::new("Failed").unwrap().as_c_str()
322 );
323
324 ffi::g_error_free(e);
325 }
326 }
327
328 #[test]
329 fn test_bool_error() {
330 let from_static_msg = bool_error!("Static message");
331 assert_eq!(from_static_msg.to_string(), "Static message");
332
333 let from_dynamic_msg = bool_error!("{} message", "Dynamic");
334 assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
335
336 let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
337 assert!(false_static_res.is_err());
338 let static_err = false_static_res.err().unwrap();
339 assert_eq!(static_err.to_string(), "Static message");
340
341 let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
342 assert!(true_static_res.is_ok());
343
344 let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
345 assert!(false_dynamic_res.is_err());
346 let dynamic_err = false_dynamic_res.err().unwrap();
347 assert_eq!(dynamic_err.to_string(), "Dynamic message");
348
349 let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
350 assert!(true_dynamic_res.is_ok());
351 }
352
353 #[test]
354 fn test_value() {
355 let e1 = Error::new(crate::FileError::Failed, "Failed");
356 let v = e1.to_value();
358 let ptr = unsafe {
360 crate::gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError
361 };
362
363 let e2 = v.get::<&Error>().unwrap();
364
365 assert_eq!(ptr, e2.to_glib_none().0);
366 }
367}