1 // Copyright 2016, The Gtk-rs Project Developers. 2 // See the COPYRIGHT file at the top-level directory of this distribution. 3 // Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT> 4 5 //! `Error` binding and helper trait. 6 7 use glib_sys; 8 use std::borrow::Cow; 9 use std::error; 10 use std::ffi::CStr; 11 use std::fmt; 12 use std::str; 13 use translate::*; 14 use Quark; 15 16 glib_wrapper! { 17 /// A generic error capable of representing various error domains (types). 18 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] 19 pub struct Error(Boxed<glib_sys::GError>); 20 21 match fn { 22 copy => |ptr| glib_sys::g_error_copy(ptr), 23 free => |ptr| glib_sys::g_error_free(ptr), 24 get_type => || glib_sys::g_error_get_type(), 25 } 26 } 27 28 unsafe impl Send for Error {} 29 unsafe impl Sync for Error {} 30 31 impl Error { 32 /// Creates an error with supplied error enum variant and message. new<T: ErrorDomain>(error: T, message: &str) -> Error33 pub fn new<T: ErrorDomain>(error: T, message: &str) -> Error { 34 unsafe { 35 from_glib_full(glib_sys::g_error_new_literal( 36 T::domain().to_glib(), 37 error.code(), 38 message.to_glib_none().0, 39 )) 40 } 41 } 42 43 /// Checks if the error domain matches `T`. is<T: ErrorDomain>(&self) -> bool44 pub fn is<T: ErrorDomain>(&self) -> bool { 45 self.0.domain == T::domain().to_glib() 46 } 47 48 /// Tries to convert to a specific error enum. 49 /// 50 /// Returns `Some` if the error belongs to the enum's error domain and 51 /// `None` otherwise. 52 /// 53 /// # Examples 54 /// 55 /// ```ignore 56 /// if let Some(file_error) = error.kind::<FileError>() { 57 /// match file_error { 58 /// FileError::Exist => ... 59 /// FileError::Isdir => ... 60 /// ... 61 /// } 62 /// } 63 /// ``` 64 /// 65 /// ```ignore 66 /// match error { 67 /// Some(FileError::Exist) => ... 68 /// Some(FileError::Isdir) => ... 69 /// ... 70 /// } 71 /// ``` kind<T: ErrorDomain>(&self) -> Option<T>72 pub fn kind<T: ErrorDomain>(&self) -> Option<T> { 73 if self.0.domain == T::domain().to_glib() { 74 T::from(self.0.code) 75 } else { 76 None 77 } 78 } 79 message(&self) -> &str80 fn message(&self) -> &str { 81 unsafe { 82 let bytes = CStr::from_ptr(self.0.message).to_bytes(); 83 str::from_utf8(bytes) 84 .unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap()) 85 } 86 } 87 } 88 89 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 91 f.write_str(self.message()) 92 } 93 } 94 95 impl error::Error for Error { description(&self) -> &str96 fn description(&self) -> &str { 97 self.message() 98 } 99 } 100 101 impl fmt::Debug for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result102 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 103 f.debug_struct("Error") 104 .field("domain", &::Quark::from_glib(self.0.domain)) 105 .field("code", &self.0.code) 106 .field("message", &self.message()) 107 .finish() 108 } 109 } 110 111 /// `GLib` error domain. 112 /// 113 /// This trait is implemented by error enums that represent error domains (types). 114 pub trait ErrorDomain: Copy { 115 /// Returns the quark identifying the error domain. 116 /// 117 /// As returned from `g_some_error_quark`. domain() -> Quark118 fn domain() -> Quark; 119 120 /// Gets the integer representation of the variant. code(self) -> i32121 fn code(self) -> i32; 122 123 /// Tries to convert an integer code to an enum variant. 124 /// 125 /// By convention, the `Failed` variant, if present, is a catch-all, 126 /// i.e. any unrecognized codes map to it. from(code: i32) -> Option<Self> where Self: Sized127 fn from(code: i32) -> Option<Self> 128 where 129 Self: Sized; 130 } 131 132 /// Generic error used for functions that fail without any further information 133 #[macro_export] 134 macro_rules! glib_bool_error( 135 // Plain strings 136 ($msg:expr) => { 137 $crate::BoolError::new($msg, file!(), module_path!(), line!()) 138 }; 139 140 // Format strings 141 ($($msg:tt)*) => { { 142 $crate::BoolError::new(format!($($msg)*), file!(), module_path!(), line!()) 143 }}; 144 ); 145 146 #[macro_export] 147 macro_rules! glib_result_from_gboolean( 148 // Plain strings 149 ($ffi_bool:expr, $msg:expr) => { 150 $crate::BoolError::from_glib($ffi_bool, $msg, file!(), module_path!(), line!()) 151 }; 152 153 // Format strings 154 ($ffi_bool:expr, $($msg:tt)*) => { { 155 $crate::BoolError::from_glib( 156 $ffi_bool, 157 format!($($msg)*), 158 file!(), 159 module_path!(), 160 line!(), 161 ) 162 }}; 163 ); 164 165 #[derive(Debug, Clone)] 166 pub struct BoolError { 167 pub message: Cow<'static, str>, 168 #[doc(hidden)] 169 pub filename: &'static str, 170 #[doc(hidden)] 171 pub function: &'static str, 172 #[doc(hidden)] 173 pub line: u32, 174 } 175 176 impl BoolError { new<Msg: Into<Cow<'static, str>>>( message: Msg, filename: &'static str, function: &'static str, line: u32, ) -> Self177 pub fn new<Msg: Into<Cow<'static, str>>>( 178 message: Msg, 179 filename: &'static str, 180 function: &'static str, 181 line: u32, 182 ) -> Self { 183 BoolError { 184 message: message.into(), 185 filename, 186 function, 187 line, 188 } 189 } 190 from_glib<Msg: Into<Cow<'static, str>>>( b: glib_sys::gboolean, message: Msg, filename: &'static str, function: &'static str, line: u32, ) -> Result<(), Self>191 pub fn from_glib<Msg: Into<Cow<'static, str>>>( 192 b: glib_sys::gboolean, 193 message: Msg, 194 filename: &'static str, 195 function: &'static str, 196 line: u32, 197 ) -> Result<(), Self> { 198 match b { 199 glib_sys::GFALSE => Err(BoolError::new(message, filename, function, line)), 200 _ => Ok(()), 201 } 202 } 203 } 204 205 impl fmt::Display for BoolError { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 207 write!( 208 f, 209 "Error {:?} in {:?} at {}:{}", 210 self.message, self.function, self.filename, self.line 211 ) 212 } 213 } 214 215 impl error::Error for BoolError { description(&self) -> &str216 fn description(&self) -> &str { 217 self.message.as_ref() 218 } 219 } 220 221 #[cfg(test)] 222 mod tests { 223 use super::*; 224 225 #[test] test_bool_error()226 fn test_bool_error() { 227 use std::error::Error; 228 229 let from_static_msg = glib_bool_error!("Static message"); 230 assert_eq!(from_static_msg.description(), "Static message"); 231 232 let from_dynamic_msg = glib_bool_error!("{} message", "Dynamic"); 233 assert_eq!(from_dynamic_msg.description(), "Dynamic message"); 234 235 let false_static_res = glib_result_from_gboolean!(glib_sys::GFALSE, "Static message"); 236 assert!(false_static_res.is_err()); 237 let static_err = false_static_res.err().unwrap(); 238 assert_eq!(static_err.description(), "Static message"); 239 240 let true_static_res = glib_result_from_gboolean!(glib_sys::GTRUE, "Static message"); 241 assert!(true_static_res.is_ok()); 242 243 let false_dynamic_res = 244 glib_result_from_gboolean!(glib_sys::GFALSE, "{} message", "Dynamic"); 245 assert!(false_dynamic_res.is_err()); 246 let dynamic_err = false_dynamic_res.err().unwrap(); 247 assert_eq!(dynamic_err.description(), "Dynamic message"); 248 249 let true_dynamic_res = glib_result_from_gboolean!(glib_sys::GTRUE, "{} message", "Dynamic"); 250 assert!(true_dynamic_res.is_ok()); 251 } 252 } 253