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 #[doc(alias = "g_error_new_literal")]
75 pub fn with_domain(domain: Quark, code: i32, message: &str) -> Error {
76 unsafe {
77 from_glib_full(ffi::g_error_new_literal(
78 domain.into_glib(),
79 code,
80 message.to_glib_none().0,
81 ))
82 }
83 }
84
85 pub fn is<T: ErrorDomain>(&self) -> bool {
88 self.inner.domain == T::domain().into_glib()
89 }
90
91 pub fn domain(&self) -> Quark {
94 unsafe { from_glib(self.inner.domain) }
95 }
96
97 pub fn code(&self) -> i32 {
100 self.inner.code
101 }
102
103 #[doc(alias = "g_error_matches")]
125 pub fn matches<T: ErrorDomain>(&self, err: T) -> bool {
126 self.is::<T>() && self.inner.code == err.code()
127 }
128
129 pub fn kind<T: ErrorDomain>(&self) -> Option<T> {
147 if self.is::<T>() {
148 T::from(self.inner.code)
149 } else {
150 None
151 }
152 }
153
154 pub fn message(&self) -> &str {
160 unsafe {
161 let bytes = CStr::from_ptr(self.inner.message).to_bytes();
162 str::from_utf8(bytes)
163 .unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap())
164 }
165 }
166}
167
168impl fmt::Display for Error {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 f.write_str(self.message())
171 }
172}
173
174impl fmt::Debug for Error {
175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176 f.debug_struct("Error")
177 .field("domain", unsafe {
178 &crate::Quark::from_glib(self.inner.domain)
179 })
180 .field("code", &self.inner.code)
181 .field("message", &self.message())
182 .finish()
183 }
184}
185
186impl error::Error for Error {}
187
188impl From<Infallible> for Error {
189 fn from(e: Infallible) -> Self {
190 match e {}
191 }
192}
193
194pub trait ErrorDomain: Copy {
199 fn domain() -> Quark;
204
205 fn code(self) -> i32;
208
209 fn from(code: i32) -> Option<Self>
215 where
216 Self: Sized;
217}
218
219#[macro_export]
222macro_rules! bool_error(
223 ($($msg:tt)*) => {{
224 match ::std::format_args!($($msg)*) {
225 formatted => {
226 if let Some(s) = formatted.as_str() {
227 $crate::BoolError::new(
228 s,
229 file!(),
230 $crate::function_name!(),
231 line!()
232 )
233 } else {
234 $crate::BoolError::new(
235 formatted.to_string(),
236 file!(),
237 $crate::function_name!(),
238 line!(),
239 )
240 }
241 }
242 }
243 }};
244);
245
246#[macro_export]
247macro_rules! result_from_gboolean(
248 ($ffi_bool:expr, $($msg:tt)*) => {{
249 match ::std::format_args!($($msg)*) {
250 formatted => {
251 if let Some(s) = formatted.as_str() {
252 $crate::BoolError::from_glib(
253 $ffi_bool,
254 s,
255 file!(),
256 $crate::function_name!(),
257 line!(),
258 )
259 } else {
260 $crate::BoolError::from_glib(
261 $ffi_bool,
262 formatted.to_string(),
263 file!(),
264 $crate::function_name!(),
265 line!(),
266 )
267 }
268 }
269 }
270
271
272 }};
273);
274
275#[derive(Debug, Clone)]
276pub struct BoolError {
277 pub message: Cow<'static, str>,
278 #[doc(hidden)]
279 pub filename: &'static str,
280 #[doc(hidden)]
281 pub function: &'static str,
282 #[doc(hidden)]
283 pub line: u32,
284}
285
286impl BoolError {
287 pub fn new(
288 message: impl Into<Cow<'static, str>>,
289 filename: &'static str,
290 function: &'static str,
291 line: u32,
292 ) -> Self {
293 Self {
294 message: message.into(),
295 filename,
296 function,
297 line,
298 }
299 }
300
301 pub fn from_glib(
302 b: ffi::gboolean,
303 message: impl Into<Cow<'static, str>>,
304 filename: &'static str,
305 function: &'static str,
306 line: u32,
307 ) -> Result<(), Self> {
308 match b {
309 ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
310 _ => Ok(()),
311 }
312 }
313}
314
315impl fmt::Display for BoolError {
316 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317 f.write_str(&self.message)
318 }
319}
320
321impl error::Error for BoolError {}
322
323#[cfg(test)]
324mod tests {
325 use std::ffi::CString;
326
327 use super::*;
328 use crate::prelude::*;
329
330 #[test]
331 fn test_error_matches() {
332 let e = Error::new(crate::FileError::Failed, "Failed");
333 assert!(e.matches(crate::FileError::Failed));
334 assert!(!e.matches(crate::FileError::Again));
335 assert!(!e.matches(crate::KeyFileError::NotFound));
336 }
337
338 #[test]
339 fn test_error_kind() {
340 let e = Error::new(crate::FileError::Failed, "Failed");
341 assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
342 assert_eq!(e.kind::<crate::KeyFileError>(), None);
343 }
344
345 #[test]
346 fn test_into_raw() {
347 unsafe {
348 let e: *mut ffi::GError =
349 Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
350 assert_eq!((*e).domain, ffi::g_file_error_quark());
351 assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
352 assert_eq!(
353 CStr::from_ptr((*e).message),
354 CString::new("Failed").unwrap().as_c_str()
355 );
356
357 ffi::g_error_free(e);
358 }
359 }
360
361 #[test]
362 fn test_bool_error() {
363 let from_static_msg = bool_error!("Static message");
364 assert_eq!(from_static_msg.to_string(), "Static message");
365
366 let from_dynamic_msg = bool_error!("{} message", "Dynamic");
367 assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
368
369 let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
370 assert!(false_static_res.is_err());
371 let static_err = false_static_res.err().unwrap();
372 assert_eq!(static_err.to_string(), "Static message");
373
374 let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
375 assert!(true_static_res.is_ok());
376
377 let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
378 assert!(false_dynamic_res.is_err());
379 let dynamic_err = false_dynamic_res.err().unwrap();
380 assert_eq!(dynamic_err.to_string(), "Dynamic message");
381
382 let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
383 assert!(true_dynamic_res.is_ok());
384 }
385
386 #[test]
387 fn test_value() {
388 let e1 = Error::new(crate::FileError::Failed, "Failed");
389 let v = e1.to_value();
391 let ptr = unsafe {
393 crate::gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError
394 };
395
396 let e2 = v.get::<&Error>().unwrap();
397
398 assert_eq!(ptr, e2.to_glib_none().0);
399 }
400
401 #[test]
402 fn test_from_quark() {
403 let original = Error::new(crate::FileError::Failed, "Original message");
404 let modified = Error::with_domain(original.domain(), original.code(), "Modified message");
405
406 assert_eq!(original.domain(), modified.domain());
408 assert_eq!(original.code(), modified.code());
409 assert!(modified.matches(crate::FileError::Failed));
410
411 assert_eq!(modified.message(), "Modified message");
413 assert_ne!(original.message(), modified.message());
414 }
415}