1use glib::{prelude::*, translate::*};
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 = T::domain();
49 let error_code = 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 ($cat:expr, $($msg:tt)*) => { {
66 $crate::LoggableError::new($cat.clone(), $crate::glib::bool_error!($($msg)*))
67 }};
68);
69
70#[macro_export]
71macro_rules! result_from_gboolean(
72 ($ffi_bool:expr, $cat:expr, $($msg:tt)*) => { {
73 $crate::glib::result_from_gboolean!($ffi_bool, $($msg)*)
74 .map_err(|bool_err| $crate::LoggableError::new($cat.clone(), bool_err))
75 }};
76);
77
78#[derive(Debug, Clone, Error)]
79#[error("Error {:?}: {:?} at {}:{}", .category.name(), .bool_error.message, .bool_error.filename, .bool_error.line)]
80pub struct LoggableError {
81 category: crate::DebugCategory,
82 bool_error: glib::BoolError,
83}
84
85impl LoggableError {
86 pub fn new(category: crate::DebugCategory, bool_error: glib::BoolError) -> LoggableError {
87 skip_assert_initialized!();
88 LoggableError {
89 category,
90 bool_error,
91 }
92 }
93
94 #[inline(never)]
95 pub fn log(&self) {
96 self.bool_error.filename.run_with_gstr(|filename| {
97 self.category.log(
98 None::<&glib::Object>,
99 crate::DebugLevel::Error,
100 filename,
101 self.bool_error.function,
102 self.bool_error.line,
103 format_args!("{}", self.bool_error.message),
104 );
105 });
106 }
107
108 pub fn log_with_object(&self, obj: &impl IsA<glib::Object>) {
109 self.log_with_object_internal(obj.as_ref());
110 }
111
112 #[inline(never)]
113 fn log_with_object_internal(&self, obj: &glib::Object) {
114 self.log_with_object_internal_and_level(obj, crate::DebugLevel::Error);
115 }
116
117 #[inline(never)]
118 fn log_with_object_internal_and_level(&self, obj: &glib::Object, level: crate::DebugLevel) {
119 self.bool_error.filename.run_with_gstr(|filename| {
120 self.category.log(
121 Some(obj),
122 level,
123 filename,
124 self.bool_error.function,
125 self.bool_error.line,
126 format_args!("{}", self.bool_error.message),
127 );
128 });
129 }
130
131 pub fn log_with_imp(&self, imp: &impl glib::subclass::types::ObjectSubclass) {
132 use glib::subclass::prelude::*;
133
134 self.log_with_object_internal_and_level(
135 unsafe { imp.obj().unsafe_cast_ref::<glib::Object>() },
136 crate::DebugLevel::Error,
137 );
138 }
139
140 pub fn log_with_imp_and_level(
141 &self,
142 imp: &impl glib::subclass::types::ObjectSubclass,
143 level: crate::DebugLevel,
144 ) {
145 use glib::subclass::prelude::*;
146
147 self.log_with_object_internal_and_level(
148 unsafe { imp.obj().unsafe_cast_ref::<glib::Object>() },
149 level,
150 );
151 }
152
153 pub fn category(&self) -> crate::DebugCategory {
154 self.category
155 }
156}
157
158impl From<glib::BoolError> for LoggableError {
159 fn from(bool_error: glib::BoolError) -> Self {
160 skip_assert_initialized!();
161 LoggableError {
162 category: *crate::CAT_RUST,
163 bool_error,
164 }
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn error_message() {
174 crate::init().unwrap();
175
176 let e = ErrorMessage::new(
177 &crate::CoreError::Failed,
178 Some("message"),
179 Some("debug"),
180 "filename",
181 "function",
182 7,
183 );
184 assert_eq!(
185 format!("{e}"),
186 "Error Some(\"message\") (Some(\"debug\")) at filename:7"
187 );
188 }
189
190 #[test]
191 fn logabble_error() {
192 crate::init().unwrap();
193
194 let e: LoggableError = glib::BoolError::new("msg", "filename", "function", 7).into();
195 assert_eq!(format!("{e}"), "Error \"GST_RUST\": \"msg\" at filename:7");
196 }
197}