glib/
error.rs

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
7use glib_sys;
8use std::borrow::Cow;
9use std::error;
10use std::ffi::CStr;
11use std::fmt;
12use std::str;
13use translate::*;
14use Quark;
15
16glib_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
28unsafe impl Send for Error {}
29unsafe impl Sync for Error {}
30
31impl Error {
32    /// Creates an error with supplied error enum variant and message.
33    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`.
44    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    /// ```
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
80    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
89impl fmt::Display for Error {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        f.write_str(self.message())
92    }
93}
94
95impl error::Error for Error {
96    fn description(&self) -> &str {
97        self.message()
98    }
99}
100
101impl fmt::Debug for Error {
102    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).
114pub trait ErrorDomain: Copy {
115    /// Returns the quark identifying the error domain.
116    ///
117    /// As returned from `g_some_error_quark`.
118    fn domain() -> Quark;
119
120    /// Gets the integer representation of the variant.
121    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.
127    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]
134macro_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]
147macro_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)]
166pub 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
176impl BoolError {
177    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
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
205impl fmt::Display for BoolError {
206    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
215impl error::Error for BoolError {
216    fn description(&self) -> &str {
217        self.message.as_ref()
218    }
219}
220
221#[cfg(test)]
222mod tests {
223    use super::*;
224
225    #[test]
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}