1use std::{borrow::Cow, convert::Infallible, error, ffi::CStr, fmt, str};
7
8use crate::{Quark, ffi, translate::*};
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 #[doc(alias = "g_prefix_error_literal")]
170 #[doc(alias = "prefix_error_literal")]
171 #[doc(alias = "g_prefix_error")]
172 #[doc(alias = "prefix_error")]
173 pub fn prefix(self, name: impl IntoGStr) -> Self {
174 let mut this = self.into_glib_ptr();
175 name.run_with_gstr(|name| unsafe {
176 match () {
177 #[cfg(feature = "v2_70")]
178 () => ffi::g_prefix_error_literal(&mut this, name.as_ptr()),
179 #[cfg(not(feature = "v2_70"))]
180 () => ffi::g_prefix_error(&mut this, c"%s".as_ptr(), name.as_ptr()),
181 }
182 from_glib_full(this)
183 })
184 }
185}
186
187impl fmt::Display for Error {
188 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
189 f.write_str(self.message())
190 }
191}
192
193impl fmt::Debug for Error {
194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
195 f.debug_struct("Error")
196 .field("domain", &unsafe {
197 crate::Quark::from_glib(self.inner.domain)
198 })
199 .field("code", &self.inner.code)
200 .field("message", &self.message())
201 .finish()
202 }
203}
204
205impl error::Error for Error {}
206
207impl From<Infallible> for Error {
208 fn from(e: Infallible) -> Self {
209 match e {}
210 }
211}
212
213pub trait ErrorDomain: Copy {
218 fn domain() -> Quark;
223
224 fn code(self) -> i32;
227
228 fn from(code: i32) -> Option<Self>
234 where
235 Self: Sized;
236}
237
238#[macro_export]
241macro_rules! bool_error(
242 ($($msg:tt)*) => {{
243 match ::std::format_args!($($msg)*) {
244 formatted => {
245 if let Some(s) = formatted.as_str() {
246 $crate::BoolError::new(
247 s,
248 file!(),
249 $crate::function_name!(),
250 line!()
251 )
252 } else {
253 $crate::BoolError::new(
254 formatted.to_string(),
255 file!(),
256 $crate::function_name!(),
257 line!(),
258 )
259 }
260 }
261 }
262 }};
263);
264
265#[macro_export]
266macro_rules! result_from_gboolean(
267 ($ffi_bool:expr, $($msg:tt)*) => {{
268 match ::std::format_args!($($msg)*) {
269 formatted => {
270 if let Some(s) = formatted.as_str() {
271 $crate::BoolError::from_glib(
272 $ffi_bool,
273 s,
274 file!(),
275 $crate::function_name!(),
276 line!(),
277 )
278 } else {
279 $crate::BoolError::from_glib(
280 $ffi_bool,
281 formatted.to_string(),
282 file!(),
283 $crate::function_name!(),
284 line!(),
285 )
286 }
287 }
288 }
289
290
291 }};
292);
293
294#[derive(Debug, Clone)]
295pub struct BoolError {
296 pub message: Cow<'static, str>,
297 #[doc(hidden)]
298 pub filename: &'static str,
299 #[doc(hidden)]
300 pub function: &'static str,
301 #[doc(hidden)]
302 pub line: u32,
303}
304
305impl BoolError {
306 pub fn new(
307 message: impl Into<Cow<'static, str>>,
308 filename: &'static str,
309 function: &'static str,
310 line: u32,
311 ) -> Self {
312 Self {
313 message: message.into(),
314 filename,
315 function,
316 line,
317 }
318 }
319
320 pub fn from_glib(
321 b: ffi::gboolean,
322 message: impl Into<Cow<'static, str>>,
323 filename: &'static str,
324 function: &'static str,
325 line: u32,
326 ) -> Result<(), Self> {
327 match b {
328 ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
329 _ => Ok(()),
330 }
331 }
332}
333
334impl fmt::Display for BoolError {
335 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
336 f.write_str(&self.message)
337 }
338}
339
340impl error::Error for BoolError {}
341
342#[cfg(test)]
343mod tests {
344 use std::ffi::CString;
345
346 use super::*;
347 use crate::prelude::*;
348
349 #[test]
350 fn test_error_matches() {
351 let e = Error::new(crate::FileError::Failed, "Failed");
352 assert!(e.matches(crate::FileError::Failed));
353 assert!(!e.matches(crate::FileError::Again));
354 assert!(!e.matches(crate::KeyFileError::NotFound));
355 }
356
357 #[test]
358 fn test_error_kind() {
359 let e = Error::new(crate::FileError::Failed, "Failed");
360 assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
361 assert_eq!(e.kind::<crate::KeyFileError>(), None);
362 }
363
364 #[test]
365 fn test_into_raw() {
366 unsafe {
367 let e: *mut ffi::GError =
368 Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
369 assert_eq!((*e).domain, ffi::g_file_error_quark());
370 assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
371 assert_eq!(
372 CStr::from_ptr((*e).message),
373 CString::new("Failed").unwrap().as_c_str()
374 );
375
376 ffi::g_error_free(e);
377 }
378 }
379
380 #[test]
381 fn test_bool_error() {
382 let from_static_msg = bool_error!("Static message");
383 assert_eq!(from_static_msg.to_string(), "Static message");
384
385 let from_dynamic_msg = bool_error!("{} message", "Dynamic");
386 assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
387
388 let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
389 assert!(false_static_res.is_err());
390 let static_err = false_static_res.err().unwrap();
391 assert_eq!(static_err.to_string(), "Static message");
392
393 let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
394 assert!(true_static_res.is_ok());
395
396 let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
397 assert!(false_dynamic_res.is_err());
398 let dynamic_err = false_dynamic_res.err().unwrap();
399 assert_eq!(dynamic_err.to_string(), "Dynamic message");
400
401 let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
402 assert!(true_dynamic_res.is_ok());
403 }
404
405 #[test]
406 fn test_value() {
407 let e1 = Error::new(crate::FileError::Failed, "Failed");
408 let v = e1.to_value();
410 let ptr = unsafe {
412 crate::gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError
413 };
414
415 let e2 = v.get::<&Error>().unwrap();
416
417 assert_eq!(ptr, e2.to_glib_none().0);
418 }
419
420 #[test]
421 fn test_from_quark() {
422 let original = Error::new(crate::FileError::Failed, "Original message");
423 let modified = Error::with_domain(original.domain(), original.code(), "Modified message");
424
425 assert_eq!(original.domain(), modified.domain());
427 assert_eq!(original.code(), modified.code());
428 assert!(modified.matches(crate::FileError::Failed));
429
430 assert_eq!(modified.message(), "Modified message");
432 assert_ne!(original.message(), modified.message());
433 }
434
435 #[test]
436 fn test_prefix() {
437 let original = Error::new(crate::FileError::Failed, "Original message");
438 let modified = original.prefix(crate::gstr!("Prefix: "));
439
440 assert!(modified.matches(crate::FileError::Failed));
442
443 assert_eq!(modified.message(), "Prefix: Original message");
445 }
446}