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 unsafe {
135 self.log_with_object_internal_and_level(
136 imp.obj().unsafe_cast_ref::<glib::Object>(),
137 crate::DebugLevel::Error,
138 );
139 }
140 }
141
142 pub fn log_with_imp_and_level(
143 &self,
144 imp: &impl glib::subclass::types::ObjectSubclass,
145 level: crate::DebugLevel,
146 ) {
147 use glib::subclass::prelude::*;
148
149 unsafe {
150 self.log_with_object_internal_and_level(
151 imp.obj().unsafe_cast_ref::<glib::Object>(),
152 level,
153 );
154 }
155 }
156
157 pub fn category(&self) -> crate::DebugCategory {
158 self.category
159 }
160}
161
162impl From<glib::BoolError> for LoggableError {
163 fn from(bool_error: glib::BoolError) -> Self {
164 skip_assert_initialized!();
165 LoggableError {
166 category: *crate::CAT_RUST,
167 bool_error,
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn error_message() {
178 crate::init().unwrap();
179
180 let e = ErrorMessage::new(
181 &crate::CoreError::Failed,
182 Some("message"),
183 Some("debug"),
184 "filename",
185 "function",
186 7,
187 );
188 assert_eq!(
189 format!("{e}"),
190 "Error Some(\"message\") (Some(\"debug\")) at filename:7"
191 );
192 }
193
194 #[test]
195 fn logabble_error() {
196 crate::init().unwrap();
197
198 let e: LoggableError = glib::BoolError::new("msg", "filename", "function", 7).into();
199 assert_eq!(format!("{e}"), "Error \"GST_RUST\": \"msg\" at filename:7");
200 }
201}