1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use glib::{prelude::*, IntoGStr};
4use thiserror::Error;
5
6#[macro_export]
7macro_rules! error_msg(
8 ($err:expr, ($($msg:tt)*), [$($dbg:tt)*]) => { {
9 $crate::ErrorMessage::new(&$err, Some(format!($($msg)*).as_ref()),
10 Some(format!($($dbg)*).as_ref()),
11 file!(), $crate::glib::function_name!(), line!())
12 }};
13 ($err:expr, ($($msg:tt)*)) => { {
14 $crate::ErrorMessage::new(&$err, Some(format!($($msg)*).as_ref()),
15 None,
16 file!(), $crate::glib::function_name!(), line!())
17 }};
18
19 ($err:expr, [$($dbg:tt)*]) => { {
20 $crate::ErrorMessage::new(&$err, None,
21 Some(format!($($dbg)*).as_ref()),
22 file!(), $crate::glib::function_name!(), line!())
23 }};
24);
25
26#[derive(Clone, Debug, PartialEq, Eq, Error)]
27#[error("Error {:?} ({:?}) at {}:{}", .message, .debug, .filename, .line)]
28pub struct ErrorMessage {
29 pub(crate) error_domain: glib::Quark,
30 pub(crate) error_code: i32,
31 pub(crate) message: Option<String>,
32 pub(crate) debug: Option<String>,
33 pub(crate) filename: &'static str,
34 pub(crate) function: &'static str,
35 pub(crate) line: u32,
36}
37
38impl ErrorMessage {
39 pub fn new<T: crate::MessageErrorDomain>(
40 error: &T,
41 message: Option<&str>,
42 debug: Option<&str>,
43 filename: &'static str,
44 function: &'static str,
45 line: u32,
46 ) -> ErrorMessage {
47 skip_assert_initialized!();
48 let error_domain: Quark = T::domain();
49 let error_code: i32 = error.code();
50
51 ErrorMessage {
52 error_domain,
53 error_code,
54 message: message.map(String::from),
55 debug: debug.map(String::from),
56 filename,
57 function,
58 line,
59 }
60 }
61}
62
63#[macro_export]
64macro_rules! loggable_error(
65// Plain strings
66 ($cat:expr, $msg:expr) => {
67 $crate::LoggableError::new($cat.clone(), $crate::glib::bool_error!($msg))
68 };
69
70// Format strings
71 ($cat:expr, $($msg:tt)*) => { {
72 $crate::LoggableError::new($cat.clone(), $crate::glib::bool_error!($($msg)*))
73 }};
74);
75
76#[macro_export]
77macro_rules! result_from_gboolean(
78// Plain strings
79 ($ffi_bool:expr, $cat:expr, $msg:expr) => {
80 $crate::glib::result_from_gboolean!($ffi_bool, $msg)
81 .map_err(|bool_err| $crate::LoggableError::new($cat.clone(), bool_err))
82 };
83
84// Format strings
85 ($ffi_bool:expr, $cat:expr, $($msg:tt)*) => { {
86 $crate::glib::result_from_gboolean!($ffi_bool, $($msg)*)
87 .map_err(|bool_err| $crate::LoggableError::new($cat.clone(), bool_err))
88 }};
89);
90
91#[derive(Debug, Clone, Error)]
92#[error("Error {:?}: {:?} at {}:{}", .category.name(), .bool_error.message, .bool_error.filename, .bool_error.line)]
93pub struct LoggableError {
94 category: crate::DebugCategory,
95 bool_error: glib::BoolError,
96}
97
98impl LoggableError {
99 pub fn new(category: crate::DebugCategory, bool_error: glib::BoolError) -> LoggableError {
100 skip_assert_initialized!();
101 LoggableError {
102 category,
103 bool_error,
104 }
105 }
106
107 #[inline(never)]
108 pub fn log(&self) {
109 self.bool_error.filename.run_with_gstr(|filename| {
110 self.category.log(
111 None::<&glib::Object>,
112 crate::DebugLevel::Error,
113 filename,
114 self.bool_error.function,
115 self.bool_error.line,
116 format_args!("{}", self.bool_error.message),
117 );
118 });
119 }
120
121 pub fn log_with_object(&self, obj: &impl IsA<glib::Object>) {
122 self.log_with_object_internal(obj.as_ref());
123 }
124
125 #[inline(never)]
126 fn log_with_object_internal(&self, obj: &glib::Object) {
127 self.bool_error.filename.run_with_gstr(|filename| {
128 self.category.log(
129 Some(obj),
130 crate::DebugLevel::Error,
131 filename,
132 self.bool_error.function,
133 self.bool_error.line,
134 format_args!("{}", self.bool_error.message),
135 );
136 });
137 }
138
139 pub fn log_with_imp(&self, imp: &impl glib::subclass::types::ObjectSubclass) {
140 use glib::subclass::prelude::*;
141
142 self.log_with_object_internal(unsafe { imp.obj().unsafe_cast_ref::<glib::Object>() });
143 }
144
145 pub fn category(&self) -> crate::DebugCategory {
146 self.category
147 }
148}
149
150impl From<glib::BoolError> for LoggableError {
151 fn from(bool_error: glib::BoolError) -> Self {
152 skip_assert_initialized!();
153 LoggableError {
154 category: *crate::CAT_RUST,
155 bool_error,
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn error_message() {
166 crate::init().unwrap();
167
168 let e = ErrorMessage::new(
169 &crate::CoreError::Failed,
170 Some("message"),
171 Some("debug"),
172 "filename",
173 "function",
174 7,
175 );
176 assert_eq!(
177 format!("{e}"),
178 "Error Some(\"message\") (Some(\"debug\")) at filename:7"
179 );
180 }
181
182 #[test]
183 fn logabble_error() {
184 crate::init().unwrap();
185
186 let e: LoggableError = glib::BoolError::new("msg", "filename", "function", 7).into();
187 assert_eq!(format!("{e}"), "Error \"GST_RUST\": \"msg\" at filename:7");
188 }
189}
190