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)]
14 #[doc(alias = "GError")]
15 pub struct Error(Boxed<ffi::GError>);
16
17 match fn {
18 copy => |ptr| ffi::g_error_copy(ptr),
19 free => |ptr| ffi::g_error_free(ptr),
20 type_ => || ffi::g_error_get_type(),
21 }
22}
23
24unsafe impl Send for Error {}
25unsafe impl Sync for Error {}
26
27impl Error {
28 #[doc(alias = "g_error_new_literal")]
31 #[doc(alias = "g_error_new")]
32 pub fn new<T: ErrorDomain>(error: T, message: &str) -> Error {
33 unsafe {
34 from_glib_full(ffi::g_error_new_literal(
35 T::domain().into_glib(),
36 error.code(),
37 message.to_glib_none().0,
38 ))
39 }
40 }
41
42 pub fn is<T: ErrorDomain>(&self) -> bool {
45 self.inner.domain == T::domain().into_glib()
46 }
47
48 pub fn domain(&self) -> Quark {
51 unsafe { from_glib(self.inner.domain) }
52 }
53
54 #[doc(alias = "g_error_matches")]
57 pub fn matches<T: ErrorDomain>(&self, err: T) -> bool {
58 self.is::<T>() && self.inner.code == err.code()
59 }
60
61 pub fn kind<T: ErrorDomain>(&self) -> Option<T> {
79 if self.is::<T>() {
80 T::from(self.inner.code)
81 } else {
82 None
83 }
84 }
85
86 pub fn message(&self) -> &str {
92 unsafe {
93 let bytes = CStr::from_ptr(self.inner.message).to_bytes();
94 str::from_utf8(bytes)
95 .unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap())
96 }
97 }
98}
99
100impl fmt::Display for Error {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 f.write_str(self.message())
103 }
104}
105
106impl fmt::Debug for Error {
107 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108 f.debug_struct("Error")
109 .field("domain", unsafe {
110 &crate::Quark::from_glib(self.inner.domain)
111 })
112 .field("code", &self.inner.code)
113 .field("message", &self.message())
114 .finish()
115 }
116}
117
118impl error::Error for Error {}
119
120impl From<Infallible> for Error {
121 fn from(e: Infallible) -> Self {
122 match e {}
123 }
124}
125
126pub trait ErrorDomain: Copy {
131 fn domain() -> Quark;
136
137 fn code(self) -> i32;
140
141 fn from(code: i32) -> Option<Self>
147 where
148 Self: Sized;
149}
150
151#[macro_export]
154macro_rules! bool_error(
155 ($($msg:tt)*) => {{
156 match ::std::format_args!($($msg)*) {
157 formatted => {
158 if let Some(s) = formatted.as_str() {
159 $crate::BoolError::new(
160 s,
161 file!(),
162 $crate::function_name!(),
163 line!()
164 )
165 } else {
166 $crate::BoolError::new(
167 formatted.to_string(),
168 file!(),
169 $crate::function_name!(),
170 line!(),
171 )
172 }
173 }
174 }
175 }};
176);
177
178#[macro_export]
179macro_rules! result_from_gboolean(
180 ($ffi_bool:expr, $($msg:tt)*) => {{
181 match ::std::format_args!($($msg)*) {
182 formatted => {
183 if let Some(s) = formatted.as_str() {
184 $crate::BoolError::from_glib(
185 $ffi_bool,
186 s,
187 file!(),
188 $crate::function_name!(),
189 line!(),
190 )
191 } else {
192 $crate::BoolError::from_glib(
193 $ffi_bool,
194 formatted.to_string(),
195 file!(),
196 $crate::function_name!(),
197 line!(),
198 )
199 }
200 }
201 }
202
203
204 }};
205);
206
207#[derive(Debug, Clone)]
208pub struct BoolError {
209 pub message: Cow<'static, str>,
210 #[doc(hidden)]
211 pub filename: &'static str,
212 #[doc(hidden)]
213 pub function: &'static str,
214 #[doc(hidden)]
215 pub line: u32,
216}
217
218impl BoolError {
219 pub fn new(
220 message: impl Into<Cow<'static, str>>,
221 filename: &'static str,
222 function: &'static str,
223 line: u32,
224 ) -> Self {
225 Self {
226 message: message.into(),
227 filename,
228 function,
229 line,
230 }
231 }
232
233 pub fn from_glib(
234 b: ffi::gboolean,
235 message: impl Into<Cow<'static, str>>,
236 filename: &'static str,
237 function: &'static str,
238 line: u32,
239 ) -> Result<(), Self> {
240 match b {
241 ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
242 _ => Ok(()),
243 }
244 }
245}
246
247impl fmt::Display for BoolError {
248 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
249 f.write_str(&self.message)
250 }
251}
252
253impl error::Error for BoolError {}
254
255#[cfg(test)]
256mod tests {
257 use std::ffi::CString;
258
259 use super::*;
260 use crate::prelude::*;
261
262 #[test]
263 fn test_error_matches() {
264 let e = Error::new(crate::FileError::Failed, "Failed");
265 assert!(e.matches(crate::FileError::Failed));
266 assert!(!e.matches(crate::FileError::Again));
267 assert!(!e.matches(crate::KeyFileError::NotFound));
268 }
269
270 #[test]
271 fn test_error_kind() {
272 let e = Error::new(crate::FileError::Failed, "Failed");
273 assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
274 assert_eq!(e.kind::<crate::KeyFileError>(), None);
275 }
276
277 #[test]
278 fn test_into_raw() {
279 unsafe {
280 let e: *mut ffi::GError =
281 Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
282 assert_eq!((*e).domain, ffi::g_file_error_quark());
283 assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
284 assert_eq!(
285 CStr::from_ptr((*e).message),
286 CString::new("Failed").unwrap().as_c_str()
287 );
288
289 ffi::g_error_free(e);
290 }
291 }
292
293 #[test]
294 fn test_bool_error() {
295 let from_static_msg = bool_error!("Static message");
296 assert_eq!(from_static_msg.to_string(), "Static message");
297
298 let from_dynamic_msg = bool_error!("{} message", "Dynamic");
299 assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
300
301 let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
302 assert!(false_static_res.is_err());
303 let static_err = false_static_res.err().unwrap();
304 assert_eq!(static_err.to_string(), "Static message");
305
306 let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
307 assert!(true_static_res.is_ok());
308
309 let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
310 assert!(false_dynamic_res.is_err());
311 let dynamic_err = false_dynamic_res.err().unwrap();
312 assert_eq!(dynamic_err.to_string(), "Dynamic message");
313
314 let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
315 assert!(true_dynamic_res.is_ok());
316 }
317
318 #[test]
319 fn test_value() {
320 let e1 = Error::new(crate::FileError::Failed, "Failed");
321 let v = e1.to_value();
323 let ptr = unsafe {
325 crate::gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError
326 };
327
328 let e2 = v.get::<&Error>().unwrap();
329
330 assert_eq!(ptr, e2.to_glib_none().0);
331 }
332}