1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{borrow::Cow, ffi::CStr, fmt, ptr};
4
5use glib::once_cell::sync::Lazy;
6use glib::{ffi::gpointer, prelude::*, translate::*, IntoGStr, IntoOptionalGStr};
7use libc::c_char;
8
9use crate::DebugLevel;
10
11#[derive(PartialEq, Eq)]
12#[doc(alias = "GstDebugMessage")]
13pub struct DebugMessage(ptr::NonNull<ffi::GstDebugMessage>);
14
15impl fmt::Debug for DebugMessage {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 f.debug_tuple(name:"DebugMessage").field(&self.get()).finish()
18 }
19}
20
21impl DebugMessage {
22 #[doc(alias = "gst_debug_message_get")]
23 #[inline]
24 pub fn get(&self) -> Option<Cow<glib::GStr>> {
25 unsafe {
26 let message = ffi::gst_debug_message_get(self.0.as_ptr());
27
28 if message.is_null() {
29 None
30 } else {
31 Some(glib::GStr::from_ptr_lossy(message))
32 }
33 }
34 }
35
36 #[cfg(feature = "v1_22")]
37 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
38 #[doc(alias = "gst_debug_message_get_id")]
39 #[inline]
40 pub fn id(&self) -> Option<&glib::GStr> {
41 unsafe {
42 let id = ffi::gst_debug_message_get_id(self.0.as_ptr());
43
44 if id.is_null() {
45 None
46 } else {
47 Some(glib::GStr::from_ptr(id))
48 }
49 }
50 }
51}
52
53#[derive(PartialEq, Eq, Clone, Copy)]
54#[doc(alias = "GstDebugCategory")]
55#[repr(transparent)]
56pub struct DebugCategory(Option<ptr::NonNull<ffi::GstDebugCategory>>);
57
58impl DebugCategory {
59 #[doc(alias = "gst_debug_category_new")]
60 #[doc(alias = "GST_DEBUG_CATEGORY")]
61 #[doc(alias = "GST_DEBUG_CATEGORY_INIT")]
62 pub fn new(
63 name: &str,
64 color: crate::DebugColorFlags,
65 description: Option<&str>,
66 ) -> DebugCategory {
67 skip_assert_initialized!();
68 extern "C" {
69 fn _gst_debug_category_new(
70 name: *const c_char,
71 color: ffi::GstDebugColorFlags,
72 description: *const c_char,
73 ) -> *mut ffi::GstDebugCategory;
74 }
75
76 // Gets the category if it exists already
77 unsafe {
78 let ptr = name.run_with_gstr(|name| {
79 description.run_with_gstr(|description| {
80 _gst_debug_category_new(
81 name.to_glib_none().0,
82 color.into_glib(),
83 description.to_glib_none().0,
84 )
85 })
86 });
87
88 // Can be NULL if the debug system is compiled out
89 DebugCategory(ptr::NonNull::new(ptr))
90 }
91 }
92
93 #[doc(alias = "gst_debug_get_category")]
94 #[inline]
95 pub fn get(name: &str) -> Option<DebugCategory> {
96 skip_assert_initialized!();
97 unsafe {
98 extern "C" {
99 fn _gst_debug_get_category(name: *const c_char) -> *mut ffi::GstDebugCategory;
100 }
101
102 let cat = name.run_with_gstr(|name| _gst_debug_get_category(name.to_glib_none().0));
103
104 if cat.is_null() {
105 None
106 } else {
107 Some(DebugCategory(Some(ptr::NonNull::new_unchecked(cat))))
108 }
109 }
110 }
111
112 #[doc(alias = "get_threshold")]
113 #[doc(alias = "gst_debug_category_get_threshold")]
114 #[inline]
115 pub fn threshold(self) -> crate::DebugLevel {
116 match self.0 {
117 Some(cat) => unsafe { from_glib(cat.as_ref().threshold) },
118 None => crate::DebugLevel::None,
119 }
120 }
121
122 #[doc(alias = "gst_debug_category_set_threshold")]
123 #[inline]
124 pub fn set_threshold(self, threshold: crate::DebugLevel) {
125 if let Some(cat) = self.0 {
126 unsafe { ffi::gst_debug_category_set_threshold(cat.as_ptr(), threshold.into_glib()) }
127 }
128 }
129
130 #[doc(alias = "gst_debug_category_reset_threshold")]
131 #[inline]
132 pub fn reset_threshold(self) {
133 if let Some(cat) = self.0 {
134 unsafe { ffi::gst_debug_category_reset_threshold(cat.as_ptr()) }
135 }
136 }
137
138 #[inline]
139 pub fn above_threshold(self, level: crate::DebugLevel) -> bool {
140 match self.0 {
141 Some(cat) => unsafe { cat.as_ref().threshold >= level.into_glib() },
142 None => false,
143 }
144 }
145
146 #[doc(alias = "get_color")]
147 #[doc(alias = "gst_debug_category_get_color")]
148 #[inline]
149 pub fn color(self) -> crate::DebugColorFlags {
150 match self.0 {
151 Some(cat) => unsafe { from_glib(cat.as_ref().color) },
152 None => crate::DebugColorFlags::empty(),
153 }
154 }
155
156 #[doc(alias = "get_name")]
157 #[doc(alias = "gst_debug_category_get_name")]
158 #[inline]
159 pub fn name<'a>(self) -> &'a str {
160 match self.0 {
161 Some(cat) => unsafe { CStr::from_ptr(cat.as_ref().name).to_str().unwrap() },
162 None => "",
163 }
164 }
165
166 #[doc(alias = "get_description")]
167 #[doc(alias = "gst_debug_category_get_description")]
168 #[inline]
169 pub fn description<'a>(self) -> Option<&'a str> {
170 let cat = self.0?;
171
172 unsafe {
173 let ptr = cat.as_ref().description;
174
175 if ptr.is_null() {
176 None
177 } else {
178 Some(CStr::from_ptr(ptr).to_str().unwrap())
179 }
180 }
181 }
182
183 #[inline]
184 #[doc(alias = "gst_debug_log")]
185 #[doc(alias = "gst_debug_log_literal")]
186 pub fn log(
187 self,
188 obj: Option<&impl IsA<glib::Object>>,
189 level: crate::DebugLevel,
190 file: &glib::GStr,
191 function: &str,
192 line: u32,
193 args: fmt::Arguments,
194 ) {
195 if !self.above_threshold(level) {
196 return;
197 }
198
199 self.log_unfiltered_internal(
200 obj.map(|obj| obj.as_ref()),
201 level,
202 file,
203 function,
204 line,
205 args,
206 )
207 }
208
209 #[inline]
210 #[doc(alias = "gst_debug_log_literal")]
211 pub fn log_literal(
212 self,
213 obj: Option<&impl IsA<glib::Object>>,
214 level: crate::DebugLevel,
215 file: &glib::GStr,
216 function: &str,
217 line: u32,
218 msg: &glib::GStr,
219 ) {
220 if !self.above_threshold(level) {
221 return;
222 }
223
224 self.log_literal_unfiltered_internal(
225 obj.map(|obj| obj.as_ref()),
226 level,
227 file,
228 function,
229 line,
230 msg,
231 )
232 }
233
234 // rustdoc-stripper-ignore-next
235 /// Logs without checking the log level.
236 #[inline]
237 #[doc(alias = "gst_debug_log")]
238 pub fn log_unfiltered(
239 self,
240 obj: Option<&impl IsA<glib::Object>>,
241 level: crate::DebugLevel,
242 file: &glib::GStr,
243 function: &str,
244 line: u32,
245 args: fmt::Arguments,
246 ) {
247 self.log_unfiltered_internal(
248 obj.map(|obj| obj.as_ref()),
249 level,
250 file,
251 function,
252 line,
253 args,
254 )
255 }
256
257 // rustdoc-stripper-ignore-next
258 /// Logs without checking the log level.
259 #[inline]
260 #[doc(alias = "gst_debug_log_literal")]
261 pub fn log_literal_unfiltered(
262 self,
263 obj: Option<&impl IsA<glib::Object>>,
264 level: crate::DebugLevel,
265 file: &glib::GStr,
266 function: &str,
267 line: u32,
268 msg: &glib::GStr,
269 ) {
270 self.log_literal_unfiltered_internal(
271 obj.map(|obj| obj.as_ref()),
272 level,
273 file,
274 function,
275 line,
276 msg,
277 )
278 }
279
280 #[inline(never)]
281 fn log_unfiltered_internal(
282 self,
283 obj: Option<&glib::Object>,
284 level: crate::DebugLevel,
285 file: &glib::GStr,
286 function: &str,
287 line: u32,
288 args: fmt::Arguments,
289 ) {
290 let mut w = smallvec::SmallVec::<[u8; 256]>::new();
291
292 // Can't really happen but better safe than sorry
293 if std::io::Write::write_fmt(&mut w, args).is_err() {
294 return;
295 }
296 w.push(0);
297
298 self.log_literal_unfiltered_internal(obj, level, file, function, line, unsafe {
299 glib::GStr::from_utf8_with_nul_unchecked(&w)
300 });
301 }
302
303 #[inline(never)]
304 fn log_literal_unfiltered_internal(
305 self,
306 obj: Option<&glib::Object>,
307 level: crate::DebugLevel,
308 file: &glib::GStr,
309 function: &str,
310 line: u32,
311 msg: &glib::GStr,
312 ) {
313 let cat = match self.0 {
314 Some(cat) => cat,
315 None => return,
316 };
317
318 let obj_ptr = match obj {
319 Some(obj) => obj.as_ptr(),
320 None => ptr::null_mut(),
321 };
322
323 function.run_with_gstr(|function| {
324 #[cfg(feature = "v1_20")]
325 unsafe {
326 ffi::gst_debug_log_literal(
327 cat.as_ptr(),
328 level.into_glib(),
329 file.as_ptr(),
330 function.as_ptr(),
331 line as i32,
332 obj_ptr,
333 msg.as_ptr(),
334 );
335 }
336 #[cfg(not(feature = "v1_20"))]
337 unsafe {
338 ffi::gst_debug_log(
339 cat.as_ptr(),
340 level.into_glib(),
341 file.as_ptr(),
342 function.as_ptr(),
343 line as i32,
344 obj_ptr,
345 b"%s\0".as_ptr() as *const _,
346 msg.as_ptr(),
347 );
348 }
349 });
350 }
351
352 #[cfg(feature = "v1_22")]
353 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
354 #[inline]
355 #[doc(alias = "gst_debug_log_id")]
356 pub fn log_id(
357 self,
358 id: impl AsRef<glib::GStr>,
359 level: crate::DebugLevel,
360 file: &glib::GStr,
361 function: &str,
362 line: u32,
363 args: fmt::Arguments,
364 ) {
365 if !self.above_threshold(level) {
366 return;
367 }
368
369 self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args);
370 }
371
372 #[cfg(feature = "v1_22")]
373 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
374 #[inline]
375 #[doc(alias = "gst_debug_log_id_literal")]
376 pub fn log_id_literal(
377 self,
378 id: impl AsRef<glib::GStr>,
379 level: crate::DebugLevel,
380 file: &glib::GStr,
381 function: &str,
382 line: u32,
383 msg: &glib::GStr,
384 ) {
385 if !self.above_threshold(level) {
386 return;
387 }
388
389 self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg);
390 }
391
392 #[cfg(feature = "v1_22")]
393 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
394 // rustdoc-stripper-ignore-next
395 /// Logs without checking the log level.
396 #[inline]
397 #[doc(alias = "gst_debug_log_id")]
398 pub fn log_id_unfiltered(
399 self,
400 id: impl AsRef<glib::GStr>,
401 level: crate::DebugLevel,
402 file: &glib::GStr,
403 function: &str,
404 line: u32,
405 args: fmt::Arguments,
406 ) {
407 self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args)
408 }
409
410 #[cfg(feature = "v1_22")]
411 #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))]
412 // rustdoc-stripper-ignore-next
413 /// Logs without checking the log level.
414 #[inline]
415 #[doc(alias = "gst_debug_log_id_literal")]
416 pub fn log_id_literal_unfiltered(
417 self,
418 id: impl AsRef<glib::GStr>,
419 level: crate::DebugLevel,
420 file: &glib::GStr,
421 function: &str,
422 line: u32,
423 msg: &glib::GStr,
424 ) {
425 self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg)
426 }
427
428 #[cfg(feature = "v1_22")]
429 #[inline(never)]
430 fn log_id_unfiltered_internal(
431 self,
432 id: &glib::GStr,
433 level: crate::DebugLevel,
434 file: &glib::GStr,
435 function: &str,
436 line: u32,
437 args: fmt::Arguments,
438 ) {
439 let mut w = smallvec::SmallVec::<[u8; 256]>::new();
440
441 // Can't really happen but better safe than sorry
442 if std::io::Write::write_fmt(&mut w, args).is_err() {
443 return;
444 }
445
446 self.log_id_literal_unfiltered_internal(id, level, file, function, line, unsafe {
447 glib::GStr::from_utf8_with_nul_unchecked(&w)
448 });
449 }
450
451 #[cfg(feature = "v1_22")]
452 #[inline(never)]
453 fn log_id_literal_unfiltered_internal(
454 self,
455 id: &glib::GStr,
456 level: crate::DebugLevel,
457 file: &glib::GStr,
458 function: &str,
459 line: u32,
460 msg: &glib::GStr,
461 ) {
462 let cat = match self.0 {
463 Some(cat) => cat,
464 None => return,
465 };
466
467 function.run_with_gstr(|function| unsafe {
468 ffi::gst_debug_log_id_literal(
469 cat.as_ptr(),
470 level.into_glib(),
471 file.as_ptr(),
472 function.as_ptr(),
473 line as i32,
474 id.as_ptr(),
475 msg.as_ptr(),
476 );
477 });
478 }
479
480 #[doc(alias = "get_all_categories")]
481 #[doc(alias = "gst_debug_get_all_categories")]
482 #[inline]
483 pub fn all_categories() -> glib::SList<DebugCategory> {
484 unsafe { glib::SList::from_glib_container(ffi::gst_debug_get_all_categories()) }
485 }
486
487 #[cfg(feature = "v1_18")]
488 #[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
489 #[doc(alias = "gst_debug_log_get_line")]
490 #[inline]
491 pub fn get_line(
492 &self,
493 level: crate::DebugLevel,
494 file: &glib::GStr,
495 function: &glib::GStr,
496 line: u32,
497 object: Option<&LoggedObject>,
498 message: &DebugMessage,
499 ) -> Option<glib::GString> {
500 let cat = self.0?;
501
502 unsafe {
503 from_glib_full(ffi::gst_debug_log_get_line(
504 cat.as_ptr(),
505 level.into_glib(),
506 file.as_ptr(),
507 function.as_ptr(),
508 line as i32,
509 object.map(|o| o.as_ptr()).unwrap_or(ptr::null_mut()),
510 message.0.as_ptr(),
511 ))
512 }
513 }
514}
515
516unsafe impl Sync for DebugCategory {}
517unsafe impl Send for DebugCategory {}
518
519impl fmt::Debug for DebugCategory {
520 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
521 f.debug_tuple(name:"DebugCategory").field(&self.name()).finish()
522 }
523}
524
525impl GlibPtrDefault for DebugCategory {
526 type GlibType = *mut ffi::GstDebugCategory;
527}
528
529unsafe impl TransparentPtrType for DebugCategory {}
530
531impl FromGlibPtrNone<*mut ffi::GstDebugCategory> for DebugCategory {
532 #[inline]
533 unsafe fn from_glib_none(ptr: *mut ffi::GstDebugCategory) -> Self {
534 debug_assert!(!ptr.is_null());
535 DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
536 }
537}
538
539impl FromGlibPtrFull<*mut ffi::GstDebugCategory> for DebugCategory {
540 #[inline]
541 unsafe fn from_glib_full(ptr: *mut ffi::GstDebugCategory) -> Self {
542 debug_assert!(!ptr.is_null());
543 DebugCategory(Some(ptr::NonNull::new_unchecked(ptr)))
544 }
545}
546
547pub static CAT_RUST: Lazy<DebugCategory> = Lazy::new(|| {
548 DebugCategory::new(
549 name:"GST_RUST",
550 color:crate::DebugColorFlags::UNDERLINE,
551 description:Some("GStreamer's Rust binding core"),
552 )
553});
554
555macro_rules! declare_debug_category_from_name(
556 ($cat:ident, $cat_name:expr) => (
557 pub static $cat: Lazy<DebugCategory> = Lazy::new(|| DebugCategory::get($cat_name)
558 .expect(&format!("Unable to find `DebugCategory` with name {}", $cat_name)));
559 );
560);
561
562declare_debug_category_from_name!(CAT_DEFAULT, "default");
563declare_debug_category_from_name!(CAT_GST_INIT, "GST_INIT");
564declare_debug_category_from_name!(CAT_MEMORY, "GST_MEMORY");
565declare_debug_category_from_name!(CAT_PARENTAGE, "GST_PARENTAGE");
566declare_debug_category_from_name!(CAT_STATES, "GST_STATES");
567declare_debug_category_from_name!(CAT_SCHEDULING, "GST_SCHEDULING");
568declare_debug_category_from_name!(CAT_BUFFER, "GST_BUFFER");
569declare_debug_category_from_name!(CAT_BUFFER_LIST, "GST_BUFFER_LIST");
570declare_debug_category_from_name!(CAT_BUS, "GST_BUS");
571declare_debug_category_from_name!(CAT_CAPS, "GST_CAPS");
572declare_debug_category_from_name!(CAT_CLOCK, "GST_CLOCK");
573declare_debug_category_from_name!(CAT_ELEMENT_PADS, "GST_ELEMENT_PADS");
574declare_debug_category_from_name!(CAT_PADS, "GST_PADS");
575declare_debug_category_from_name!(CAT_PERFORMANCE, "GST_PERFORMANCE");
576declare_debug_category_from_name!(CAT_PIPELINE, "GST_PIPELINE");
577declare_debug_category_from_name!(CAT_PLUGIN_LOADING, "GST_PLUGIN_LOADING");
578declare_debug_category_from_name!(CAT_PLUGIN_INFO, "GST_PLUGIN_INFO");
579declare_debug_category_from_name!(CAT_PROPERTIES, "GST_PROPERTIES");
580declare_debug_category_from_name!(CAT_NEGOTIATION, "GST_NEGOTIATION");
581declare_debug_category_from_name!(CAT_REFCOUNTING, "GST_REFCOUNTING");
582declare_debug_category_from_name!(CAT_ERROR_SYSTEM, "GST_ERROR_SYSTEM");
583declare_debug_category_from_name!(CAT_EVENT, "GST_EVENT");
584declare_debug_category_from_name!(CAT_MESSAGE, "GST_MESSAGE");
585declare_debug_category_from_name!(CAT_PARAMS, "GST_PARAMS");
586declare_debug_category_from_name!(CAT_CALL_TRACE, "GST_CALL_TRACE");
587declare_debug_category_from_name!(CAT_SIGNAL, "GST_SIGNAL");
588declare_debug_category_from_name!(CAT_PROBE, "GST_PROBE");
589declare_debug_category_from_name!(CAT_REGISTRY, "GST_REGISTRY");
590declare_debug_category_from_name!(CAT_QOS, "GST_QOS");
591declare_debug_category_from_name!(CAT_META, "GST_META");
592declare_debug_category_from_name!(CAT_LOCKING, "GST_LOCKING");
593declare_debug_category_from_name!(CAT_CONTEXT, "GST_CONTEXT");
594
595#[macro_export]
596macro_rules! error(
597 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
598 $crate::log_with_level!($cat, level: $crate::DebugLevel::Error, obj: $obj, $($args)*)
599 }};
600 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
601 $crate::log_with_level!($cat, level: $crate::DebugLevel::Error, imp: $imp, $($args)*)
602 }};
603 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
604 $crate::log_with_level!($cat, level: $crate::DebugLevel::Error, id: $id, $($args)*)
605 }};
606 ($cat:expr, $($args:tt)*) => { {
607 $crate::log_with_level!($cat, level: $crate::DebugLevel::Error, $($args)*)
608 }};
609);
610
611#[macro_export]
612macro_rules! warning(
613 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
614 $crate::log_with_level!($cat, level: $crate::DebugLevel::Warning, obj: $obj, $($args)*)
615 }};
616 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
617 $crate::log_with_level!($cat, level: $crate::DebugLevel::Warning, imp: $imp, $($args)*)
618 }};
619 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
620 $crate::log_with_level!($cat, level: $crate::DebugLevel::Warning, id: $id, $($args)*)
621 }};
622 ($cat:expr, $($args:tt)*) => { {
623 $crate::log_with_level!($cat, level: $crate::DebugLevel::Warning, $($args)*)
624 }};
625);
626
627#[macro_export]
628macro_rules! fixme(
629 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
630 $crate::log_with_level!($cat, level: $crate::DebugLevel::Fixme, obj: $obj, $($args)*)
631 }};
632 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
633 $crate::log_with_level!($cat, level: $crate::DebugLevel::Fixme, imp: $imp, $($args)*)
634 }};
635 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
636 $crate::log_with_level!($cat, level: $crate::DebugLevel::Fixme, id: $id, $($args)*)
637 }};
638 ($cat:expr, $($args:tt)*) => { {
639 $crate::log_with_level!($cat, level: $crate::DebugLevel::Fixme, $($args)*)
640 }};
641);
642
643#[macro_export]
644macro_rules! info(
645 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
646 $crate::log_with_level!($cat, level: $crate::DebugLevel::Info, obj: $obj, $($args)*)
647 }};
648 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
649 $crate::log_with_level!($cat, level: $crate::DebugLevel::Info, imp: $imp, $($args)*)
650 }};
651 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
652 $crate::log_with_level!($cat, level: $crate::DebugLevel::Info, id: $id, $($args)*)
653 }};
654 ($cat:expr, $($args:tt)*) => { {
655 $crate::log_with_level!($cat, level: $crate::DebugLevel::Info, $($args)*)
656 }};
657);
658
659#[macro_export]
660macro_rules! debug(
661 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
662 $crate::log_with_level!($cat, level: $crate::DebugLevel::Debug, obj: $obj, $($args)*)
663 }};
664 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
665 $crate::log_with_level!($cat, level: $crate::DebugLevel::Debug, imp: $imp, $($args)*)
666 }};
667 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
668 $crate::log_with_level!($cat, level: $crate::DebugLevel::Debug, id: $id, $($args)*)
669 }};
670 ($cat:expr, $($args:tt)*) => { {
671 $crate::log_with_level!($cat, level: $crate::DebugLevel::Debug, $($args)*)
672 }};
673);
674
675#[macro_export]
676macro_rules! log(
677 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
678 $crate::log_with_level!($cat, level: $crate::DebugLevel::Log, obj: $obj, $($args)*)
679 }};
680 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
681 $crate::log_with_level!($cat, level: $crate::DebugLevel::Log, imp: $imp, $($args)*)
682 }};
683 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
684 $crate::log_with_level!($cat, level: $crate::DebugLevel::Log, id: $id, $($args)*)
685 }};
686 ($cat:expr, $($args:tt)*) => { {
687 $crate::log_with_level!($cat, level: $crate::DebugLevel::Log, $($args)*)
688 }};
689);
690
691#[macro_export]
692macro_rules! trace(
693 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
694 $crate::log_with_level!($cat, level: $crate::DebugLevel::Trace, obj: $obj, $($args)*)
695 }};
696 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
697 $crate::log_with_level!($cat, level: $crate::DebugLevel::Trace, imp: $imp, $($args)*)
698 }};
699 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
700 $crate::log_with_level!($cat, level: $crate::DebugLevel::Trace, id: $id, $($args)*)
701 }};
702 ($cat:expr, $($args:tt)*) => { {
703 $crate::log_with_level!($cat, level: $crate::DebugLevel::Trace, $($args)*)
704 }};
705);
706
707#[macro_export]
708macro_rules! memdump(
709 ($cat:expr, obj: $obj:expr, $($args:tt)*) => { {
710 $crate::log_with_level!($cat, level: $crate::DebugLevel::Memdump, obj: $obj, $($args)*)
711 }};
712 ($cat:expr, imp: $imp:expr, $($args:tt)*) => { {
713 $crate::log_with_level!($cat, level: $crate::DebugLevel::Memdump, imp: $imp, $($args)*)
714 }};
715 ($cat:expr, id: $id:expr, $($args:tt)*) => { {
716 $crate::log_with_level!($cat, level: $crate::DebugLevel::Memdump, id: $id, $($args)*)
717 }};
718 ($cat:expr, $($args:tt)*) => { {
719 $crate::log_with_level!($cat, level: $crate::DebugLevel::Memdump, $($args)*)
720 }};
721);
722
723#[macro_export]
724macro_rules! log_with_level(
725 ($cat:expr, level: $level:expr, obj: $obj:expr, $msg:literal) => { {
726 let cat = $cat.clone();
727
728 // Check the log level before using `format_args!` otherwise
729 // formatted arguments are evaluated even if we end up not logging.
730 #[allow(unused_unsafe)]
731 #[allow(clippy::redundant_closure_call)]
732 if cat.above_threshold($level) {
733 use $crate::glib::Cast;
734
735 // FIXME: Once there's a function_name! macro that returns a string literal we can
736 // directly pass it as `&GStr` forward
737
738 let obj = unsafe { $obj.unsafe_cast_ref::<$crate::glib::Object>() };
739 let function_name = $crate::glib::function_name!();
740
741 // Check if formatting is necessary or not
742 // FIXME: This needs to be a closure because the return value of format_args!() can't
743 // be assigned to a variable
744 (|args: std::fmt::Arguments| {
745 if args.as_str().is_some() {
746 $crate::DebugCategory::log_literal_unfiltered(
747 cat,
748 Some(obj),
749 $level,
750 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
751 function_name,
752 line!(),
753 $crate::glib::gstr!($msg),
754 )
755 } else {
756 $crate::DebugCategory::log_unfiltered(
757 cat,
758 Some(obj),
759 $level,
760 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
761 function_name,
762 line!(),
763 args,
764 )
765 }
766 })(format_args!($msg))
767 }
768 }};
769 ($cat:expr, level: $level:expr, obj: $obj:expr, $($args:tt)*) => { {
770 let cat = $cat.clone();
771
772 // Check the log level before using `format_args!` otherwise
773 // formatted arguments are evaluated even if we end up not logging.
774 #[allow(unused_unsafe)]
775 if cat.above_threshold($level) {
776 use $crate::glib::Cast;
777
778 // FIXME: Once there's a function_name! macro that returns a string literal we can
779 // directly pass it as `&GStr` forward
780
781 let obj = unsafe { $obj.unsafe_cast_ref::<$crate::glib::Object>() };
782 $crate::DebugCategory::log_unfiltered(
783 cat,
784 Some(obj),
785 $level,
786 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
787 $crate::glib::function_name!(),
788 line!(),
789 format_args!($($args)*),
790 )
791 }
792 }};
793 ($cat:expr, level: $level:expr, imp: $imp:expr, $msg:literal) => { {
794 let cat = $cat.clone();
795
796 // Check the log level before using `format_args!` otherwise
797 // formatted arguments are evaluated even if we end up not logging.
798 #[allow(unused_unsafe)]
799 #[allow(clippy::redundant_closure_call)]
800 if cat.above_threshold($level) {
801 use $crate::glib::Cast;
802
803 // FIXME: Once there's a function_name! macro that returns a string literal we can
804 // directly pass it as `&GStr` forward
805
806 let obj = $imp.obj();
807 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
808 let function_name = $crate::glib::function_name!();
809
810 // Check if formatting is necessary or not
811 // FIXME: This needs to be a closure because the return value of format_args!() can't
812 // be assigned to a variable
813 (|args: std::fmt::Arguments| {
814 if args.as_str().is_some() {
815 $crate::DebugCategory::log_literal_unfiltered(
816 cat,
817 Some(obj),
818 $level,
819 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
820 function_name,
821 line!(),
822 $crate::glib::gstr!($msg),
823 )
824 } else {
825 $crate::DebugCategory::log_unfiltered(
826 cat,
827 Some(obj),
828 $level,
829 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
830 function_name,
831 line!(),
832 args,
833 )
834 }
835 })(format_args!($msg))
836 }
837 }};
838 ($cat:expr, level: $level:expr, imp: $imp:expr, $($args:tt)*) => { {
839 let cat = $cat.clone();
840
841 // Check the log level before using `format_args!` otherwise
842 // formatted arguments are evaluated even if we end up not logging.
843 #[allow(unused_unsafe)]
844 if cat.above_threshold($level) {
845 use $crate::glib::Cast;
846
847 // FIXME: Once there's a function_name! macro that returns a string literal we can
848 // directly pass it as `&GStr` forward
849
850 let obj = $imp.obj();
851 let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() };
852 $crate::DebugCategory::log_unfiltered(
853 cat,
854 Some(obj),
855 $level,
856 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
857 $crate::glib::function_name!(),
858 line!(),
859 format_args!($($args)*),
860 )
861 }
862 }};
863 ($cat:expr, level: $level:expr, id: $id:literal, $msg:literal) => { {
864 let cat = $cat.clone();
865
866 // Check the log level before using `format_args!` otherwise
867 // formatted arguments are evaluated even if we end up not logging.
868 #[allow(unused_unsafe)]
869 #[allow(clippy::redundant_closure_call)]
870 if cat.above_threshold($level) {
871 // FIXME: Once there's a function_name! macro that returns a string literal we can
872 // directly pass it as `&GStr` forward
873
874 let function_name = $crate::glib::function_name!();
875
876 // Check if formatting is necessary or not
877 // FIXME: This needs to be a closure because the return value of format_args!() can't
878 // be assigned to a variable
879 (|args: std::fmt::Arguments| {
880 if args.as_str().is_some() {
881 $crate::DebugCategory::log_id_literal_unfiltered(
882 cat,
883 $crate::glib::gstr!($id),
884 $level,
885 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
886 function_name,
887 line!(),
888 $crate::glib::gstr!($msg),
889 )
890 } else {
891 $crate::DebugCategory::log_id_unfiltered(
892 cat,
893 $crate::glib::gstr!($id),
894 $level,
895 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
896 function_name,
897 line!(),
898 args,
899 )
900 }
901 })(format_args!($msg))
902 }
903 }};
904 ($cat:expr, level: $level:expr, id: $id:literal, $($args:tt)*) => { {
905 let cat = $cat.clone();
906
907 // Check the log level before using `format_args!` otherwise
908 // formatted arguments are evaluated even if we end up not logging.
909 #[allow(unused_unsafe)]
910 if cat.above_threshold($level) {
911 // FIXME: Once there's a function_name! macro that returns a string literal we can
912 // directly pass it as `&GStr` forward
913
914 $crate::DebugCategory::log_id_unfiltered(
915 cat,
916 $crate::glib::gstr!($id),
917 $level,
918 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
919 $crate::glib::function_name!(),
920 line!(),
921 format_args!($($args)*),
922 )
923 }
924 }};
925 ($cat:expr, level: $level:expr, id: $id:expr, $msg:literal) => { {
926 let cat = $cat.clone();
927
928 // Check the log level before using `format_args!` otherwise
929 // formatted arguments are evaluated even if we end up not logging.
930 #[allow(unused_unsafe)]
931 #[allow(clippy::redundant_closure_call)]
932 if cat.above_threshold($level) {
933 // FIXME: Once there's a function_name! macro that returns a string literal we can
934 // directly pass it as `&GStr` forward
935
936 let function_name = $crate::glib::function_name!();
937
938 // Check if formatting is necessary or not
939 // FIXME: This needs to be a closure because the return value of format_args!() can't
940 // be assigned to a variable
941 (|args: std::fmt::Arguments| {
942 if args.as_str().is_some() {
943 $crate::DebugCategory::log_id_literal_unfiltered(
944 cat,
945 $id,
946 $level,
947 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
948 function_name,
949 line!(),
950 $crate::glib::gstr!($msg),
951 )
952 } else {
953 $crate::DebugCategory::log_id_unfiltered(
954 cat,
955 $id,
956 $level,
957 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
958 function_name,
959 line!(),
960 args,
961 )
962 }
963 })(format_args!($msg))
964 }
965 }};
966 ($cat:expr, level: $level:expr, id: $id:expr, $($args:tt)*) => { {
967 let cat = $cat.clone();
968
969 // Check the log level before using `format_args!` otherwise
970 // formatted arguments are evaluated even if we end up not logging.
971 #[allow(unused_unsafe)]
972 if cat.above_threshold($level) {
973 // FIXME: Once there's a function_name! macro that returns a string literal we can
974 // directly pass it as `&GStr` forward
975
976 $crate::DebugCategory::log_id_unfiltered(
977 cat,
978 $id,
979 $level,
980 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
981 $crate::glib::function_name!(),
982 line!(),
983 format_args!($($args)*),
984 )
985 }
986 }};
987 ($cat:expr, level: $level:expr, $msg:literal) => { {
988 let cat = $cat.clone();
989
990 // Check the log level before using `format_args!` otherwise
991 // formatted arguments are evaluated even if we end up not logging.
992 #[allow(unused_unsafe)]
993 #[allow(clippy::redundant_closure_call)]
994 if cat.above_threshold($level) {
995 // FIXME: Once there's a function_name! macro that returns a string literal we can
996 // directly pass it as `&GStr` forward
997
998 let function_name = $crate::glib::function_name!();
999
1000 // Check if formatting is necessary or not
1001 // FIXME: This needs to be a closure because the return value of format_args!() can't
1002 // be assigned to a variable
1003 (|args: std::fmt::Arguments| {
1004 if args.as_str().is_some() {
1005 $crate::DebugCategory::log_literal_unfiltered(
1006 cat,
1007 None as Option<&$crate::glib::Object>,
1008 $level,
1009 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1010 function_name,
1011 line!(),
1012 $crate::glib::gstr!($msg),
1013 )
1014 } else {
1015 $crate::DebugCategory::log_unfiltered(
1016 cat,
1017 None as Option<&$crate::glib::Object>,
1018 $level,
1019 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1020 function_name,
1021 line!(),
1022 args,
1023 )
1024 }
1025 })(format_args!($msg))
1026 }
1027 }};
1028 ($cat:expr, level: $level:expr, $($args:tt)*) => { {
1029 let cat = $cat.clone();
1030
1031 // Check the log level before using `format_args!` otherwise
1032 // formatted arguments are evaluated even if we end up not logging.
1033 #[allow(unused_unsafe)]
1034 if cat.above_threshold($level) {
1035 // FIXME: Once there's a function_name! macro that returns a string literal we can
1036 // directly pass it as `&GStr` forward
1037
1038 $crate::DebugCategory::log_unfiltered(
1039 cat,
1040 None as Option<&$crate::glib::Object>,
1041 $level,
1042 unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) },
1043 $crate::glib::function_name!(),
1044 line!(),
1045 format_args!($($args)*),
1046 )
1047 }
1048 }};
1049);
1050
1051unsafe extern "C" fn log_handler<T>(
1052 category: *mut ffi::GstDebugCategory,
1053 level: ffi::GstDebugLevel,
1054 file: *const c_char,
1055 function: *const c_char,
1056 line: i32,
1057 object: *mut glib::gobject_ffi::GObject,
1058 message: *mut ffi::GstDebugMessage,
1059 user_data: gpointer,
1060) where
1061 T: Fn(
1062 DebugCategory,
1063 DebugLevel,
1064 &glib::GStr,
1065 &glib::GStr,
1066 u32,
1067 Option<&LoggedObject>,
1068 &DebugMessage,
1069 ) + Send
1070 + Sync
1071 + 'static,
1072{
1073 if category.is_null() {
1074 return;
1075 }
1076 let category: DebugCategory = DebugCategory(Some(ptr::NonNull::new_unchecked(ptr:category)));
1077 let level: DebugLevel = from_glib(val:level);
1078 let file: &GStr = glib::GStr::from_ptr(file);
1079 let function: &GStr = glib::GStr::from_ptr(function);
1080 let line: u32 = line as u32;
1081 let object: Option = ptr::NonNull::new(ptr:object).map(LoggedObject);
1082 let message: DebugMessage = DebugMessage(ptr::NonNull::new_unchecked(ptr:message));
1083 let handler: &T = &*(user_data as *mut T);
1084 (handler)(
1085 category,
1086 level,
1087 file,
1088 function,
1089 line,
1090 object.as_ref(),
1091 &message,
1092 );
1093}
1094
1095unsafe extern "C" fn log_handler_data_free<T>(data: gpointer) {
1096 let data: Box = Box::from_raw(data as *mut T);
1097 drop(data);
1098}
1099
1100#[derive(Debug)]
1101pub struct DebugLogFunction(ptr::NonNull<std::os::raw::c_void>);
1102
1103// The contained pointer is never dereferenced and has no thread affinity.
1104// It may be convenient to send it or share it between threads to allow cleaning
1105// up log functions from other threads than the one that created it.
1106unsafe impl Send for DebugLogFunction {}
1107unsafe impl Sync for DebugLogFunction {}
1108
1109#[derive(Debug)]
1110#[doc(alias = "GObject")]
1111pub struct LoggedObject(ptr::NonNull<glib::gobject_ffi::GObject>);
1112
1113impl LoggedObject {
1114 #[inline]
1115 pub fn as_ptr(&self) -> *mut glib::gobject_ffi::GObject {
1116 self.0.as_ptr()
1117 }
1118}
1119
1120impl fmt::Display for LoggedObject {
1121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1122 unsafe {
1123 let ptr = self.0.as_ptr();
1124 let g_type_instance = &mut (*ptr).g_type_instance;
1125 if glib::gobject_ffi::g_type_check_instance_is_fundamentally_a(
1126 g_type_instance,
1127 glib::gobject_ffi::g_object_get_type(),
1128 ) != glib::ffi::GFALSE
1129 {
1130 let type_ = (*g_type_instance.g_class).g_type;
1131
1132 if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_pad_get_type())
1133 != glib::ffi::GFALSE
1134 {
1135 let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1136 let name = if name_ptr.is_null() {
1137 "<null>"
1138 } else {
1139 CStr::from_ptr(name_ptr)
1140 .to_str()
1141 .unwrap_or("<invalid name>")
1142 };
1143
1144 let parent_ptr = (*(ptr as *mut ffi::GstObject)).parent;
1145 let parent_name = if parent_ptr.is_null() {
1146 "<null>"
1147 } else {
1148 let name_ptr = (*(parent_ptr)).name;
1149 if name_ptr.is_null() {
1150 "<null>"
1151 } else {
1152 CStr::from_ptr(name_ptr)
1153 .to_str()
1154 .unwrap_or("<invalid name>")
1155 }
1156 };
1157
1158 write!(f, "{parent_name}:{name}")
1159 } else if glib::gobject_ffi::g_type_is_a(type_, ffi::gst_object_get_type())
1160 != glib::ffi::GFALSE
1161 {
1162 let name_ptr = (*(ptr as *mut ffi::GstObject)).name;
1163 let name = if name_ptr.is_null() {
1164 "<null>"
1165 } else {
1166 CStr::from_ptr(name_ptr)
1167 .to_str()
1168 .unwrap_or("<invalid name>")
1169 };
1170 write!(f, "{name}")
1171 } else {
1172 let type_name = CStr::from_ptr(glib::gobject_ffi::g_type_name(type_));
1173 write!(
1174 f,
1175 "{}:{:?}",
1176 type_name.to_str().unwrap_or("<invalid type>"),
1177 ptr
1178 )
1179 }
1180 } else {
1181 write!(f, "{ptr:?}")
1182 }
1183 }
1184 }
1185}
1186
1187#[doc(alias = "gst_debug_add_log_function")]
1188pub fn debug_add_log_function<T>(function: T) -> DebugLogFunction
1189where
1190 T: Fn(
1191 DebugCategory,
1192 DebugLevel,
1193 &glib::GStr,
1194 &glib::GStr,
1195 u32,
1196 Option<&LoggedObject>,
1197 &DebugMessage,
1198 ) + Send
1199 + Sync
1200 + 'static,
1201{
1202 skip_assert_initialized!();
1203 unsafe {
1204 let user_data: Box = Box::new(function);
1205 let user_data_ptr: *mut c_void = Box::into_raw(user_data) as gpointer;
1206 ffi::gst_debug_add_log_function(
1207 func:Some(log_handler::<T>),
1208 user_data_ptr,
1209 notify:Some(log_handler_data_free::<T>),
1210 );
1211 DebugLogFunction(ptr::NonNull::new_unchecked(user_data_ptr))
1212 }
1213}
1214
1215pub fn debug_remove_default_log_function() {
1216 skip_assert_initialized!();
1217 unsafe {
1218 ffi::gst_debug_remove_log_function(func:None);
1219 }
1220}
1221
1222#[doc(alias = "gst_debug_remove_log_function_by_data")]
1223pub fn debug_remove_log_function(log_fn: DebugLogFunction) {
1224 skip_assert_initialized!();
1225 unsafe {
1226 ffi::gst_debug_remove_log_function_by_data(log_fn.0.as_ptr());
1227 }
1228}
1229
1230#[cfg(test)]
1231mod tests {
1232 use std::sync::{mpsc, Arc, Mutex};
1233
1234 use super::*;
1235
1236 #[test]
1237 #[doc(alias = "get_existing")]
1238 fn existing() {
1239 crate::init().unwrap();
1240
1241 let perf_cat = DebugCategory::get("GST_PERFORMANCE")
1242 .expect("Unable to find `DebugCategory` with name \"GST_PERFORMANCE\"");
1243 assert_eq!(perf_cat.name(), CAT_PERFORMANCE.name());
1244 }
1245
1246 #[test]
1247 fn all() {
1248 crate::init().unwrap();
1249
1250 assert!(DebugCategory::all_categories()
1251 .iter()
1252 .any(|c| c.name() == "GST_PERFORMANCE"));
1253 }
1254
1255 #[test]
1256 fn new_and_log() {
1257 crate::init().unwrap();
1258
1259 let cat = DebugCategory::new(
1260 "test-cat",
1261 crate::DebugColorFlags::empty(),
1262 Some("some debug category"),
1263 );
1264
1265 error!(cat, "meh");
1266 warning!(cat, "meh");
1267 fixme!(cat, "meh");
1268 info!(cat, "meh");
1269 debug!(cat, "meh");
1270 log!(cat, "meh");
1271 trace!(cat, "meh");
1272 memdump!(cat, "meh");
1273
1274 let obj = crate::Bin::with_name("meh");
1275
1276 error!(cat, obj: &obj, "meh");
1277 warning!(cat, obj: &obj, "meh");
1278 fixme!(cat, obj: &obj, "meh");
1279 info!(cat, obj: &obj, "meh");
1280 debug!(cat, obj: &obj, "meh");
1281 log!(cat, obj: &obj, "meh");
1282 trace!(cat, obj: &obj, "meh");
1283 memdump!(cat, obj: &obj, "meh");
1284
1285 error!(cat, obj: obj, "meh");
1286 warning!(cat, obj: obj, "meh");
1287 fixme!(cat, obj: obj, "meh");
1288 info!(cat, obj: obj, "meh");
1289 debug!(cat, obj: obj, "meh");
1290 log!(cat, obj: obj, "meh");
1291 trace!(cat, obj: obj, "meh");
1292 memdump!(cat, obj: obj, "meh");
1293 }
1294
1295 #[test]
1296 fn log_handler() {
1297 crate::init().unwrap();
1298
1299 let cat = DebugCategory::new(
1300 "test-cat-log",
1301 crate::DebugColorFlags::empty(),
1302 Some("some debug category"),
1303 );
1304 cat.set_threshold(DebugLevel::Info);
1305 let obj = crate::Bin::with_name("meh");
1306
1307 let (sender, receiver) = mpsc::channel();
1308
1309 let sender = Arc::new(Mutex::new(sender));
1310
1311 let handler = move |category: DebugCategory,
1312 level: DebugLevel,
1313 _file: &glib::GStr,
1314 _function: &glib::GStr,
1315 _line: u32,
1316 _object: Option<&LoggedObject>,
1317 message: &DebugMessage| {
1318 let cat = DebugCategory::get("test-cat-log").unwrap();
1319
1320 if category != cat {
1321 // This test can run in parallel with other tests, including new_and_log above.
1322 // We cannot be certain we only see our own messages.
1323 return;
1324 }
1325
1326 assert_eq!(level, DebugLevel::Info);
1327 assert_eq!(message.get().unwrap().as_ref(), "meh");
1328 let _ = sender.lock().unwrap().send(());
1329 };
1330
1331 debug_remove_default_log_function();
1332 let log_fn = debug_add_log_function(handler);
1333 info!(cat, obj: &obj, "meh");
1334
1335 receiver.recv().unwrap();
1336
1337 debug_remove_log_function(log_fn);
1338
1339 info!(cat, obj: &obj, "meh2");
1340 assert!(receiver.recv().is_err());
1341 }
1342
1343 #[test]
1344 fn no_argument_evaluation() {
1345 crate::init().unwrap();
1346
1347 let cat = DebugCategory::new(
1348 "no_argument_evaluation",
1349 crate::DebugColorFlags::empty(),
1350 Some("No Argument Evaluation debug category"),
1351 );
1352
1353 let mut arg_evaluated = false;
1354 trace!(cat, "{}", {
1355 arg_evaluated = true;
1356 "trace log"
1357 });
1358
1359 assert!(!arg_evaluated);
1360 }
1361
1362 #[cfg(feature = "v1_22")]
1363 #[test]
1364 fn id_logging() {
1365 crate::init().unwrap();
1366
1367 let cat = DebugCategory::new(
1368 "log_with_id_test_category",
1369 crate::DebugColorFlags::empty(),
1370 Some("Blablabla"),
1371 );
1372
1373 trace!(cat, id: "123", "test");
1374 trace!(cat, id: glib::GString::from("123"), "test");
1375 trace!(cat, id: &glib::GString::from("123"), "test");
1376 }
1377}
1378