diff --git a/gstreamer/Gir.toml b/gstreamer/Gir.toml index eb4e9e3b3..4348f159c 100644 --- a/gstreamer/Gir.toml +++ b/gstreamer/Gir.toml @@ -58,6 +58,8 @@ manual = [ "Gst.DeviceProviderClass", # for docs only "Gst.ElementClass", # for docs only "Gst.IdStr", + "Gst.LogContext", + "Gst.LogContextBuilder", "Gst.Rank", "Gst.Segment", "Gst.StaticCaps", @@ -191,6 +193,10 @@ status = "generate" pattern = "debug_log.*" ignore = true + [[object.function]] + pattern = "log_context.*" + ignore = true + [[object.function]] name = "debug_set_color_mode" ignore = true @@ -1504,6 +1510,20 @@ status = "generate" name = "num_errors" ignore = true +[[object]] +name = "Gst.LogContextFlags" +status = "generate" + [[object.member]] + name = "none" + ignore = true + +[[object]] +name = "Gst.LogContextHashFlags" +status = "generate" + [[object.member]] + name = "default" + ignore = true + [[object]] name = "Gst.Memory" status = "manual" @@ -2547,6 +2567,7 @@ status = "generate" [[object.derive]] name = "Debug" + [[object]] name = "Gst.URIType" status = "generate" diff --git a/gstreamer/src/auto/flags.rs b/gstreamer/src/auto/flags.rs index 0f8715909..11eaab91a 100644 --- a/gstreamer/src/auto/flags.rs +++ b/gstreamer/src/auto/flags.rs @@ -980,6 +980,218 @@ impl From for glib::Value { } } +#[cfg(feature = "v1_28")] +bitflags! { + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[doc(alias = "GstLogContextFlags")] + pub struct LogContextFlags: u32 { + #[doc(alias = "GST_LOG_CONTEXT_FLAG_THROTTLE")] + const THROTTLE = ffi::GST_LOG_CONTEXT_FLAG_THROTTLE as _; + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +#[doc(hidden)] +impl IntoGlib for LogContextFlags { + type GlibType = ffi::GstLogContextFlags; + + #[inline] + fn into_glib(self) -> ffi::GstLogContextFlags { + self.bits() + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +#[doc(hidden)] +impl FromGlib for LogContextFlags { + #[inline] + unsafe fn from_glib(value: ffi::GstLogContextFlags) -> Self { + skip_assert_initialized!(); + Self::from_bits_truncate(value) + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl StaticType for LogContextFlags { + #[inline] + #[doc(alias = "gst_log_context_flags_get_type")] + fn static_type() -> glib::Type { + unsafe { from_glib(ffi::gst_log_context_flags_get_type()) } + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl glib::HasParamSpec for LogContextFlags { + type ParamSpec = glib::ParamSpecFlags; + type SetValue = Self; + type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder; + + fn param_spec_builder() -> Self::BuilderFn { + Self::ParamSpec::builder + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl glib::value::ValueType for LogContextFlags { + type Type = Self; +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +unsafe impl<'a> glib::value::FromValue<'a> for LogContextFlags { + type Checker = glib::value::GenericValueTypeChecker; + + #[inline] + unsafe fn from_value(value: &'a glib::Value) -> Self { + skip_assert_initialized!(); + from_glib(glib::gobject_ffi::g_value_get_flags(value.to_glib_none().0)) + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl ToValue for LogContextFlags { + #[inline] + fn to_value(&self) -> glib::Value { + let mut value = glib::Value::for_value_type::(); + unsafe { + glib::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib()); + } + value + } + + #[inline] + fn value_type(&self) -> glib::Type { + Self::static_type() + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl From for glib::Value { + #[inline] + fn from(v: LogContextFlags) -> Self { + skip_assert_initialized!(); + ToValue::to_value(&v) + } +} + +#[cfg(feature = "v1_28")] +bitflags! { + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] + #[doc(alias = "GstLogContextHashFlags")] + pub struct LogContextHashFlags: u32 { + #[doc(alias = "GST_LOG_CONTEXT_IGNORE_OBJECT")] + const IGNORE_OBJECT = ffi::GST_LOG_CONTEXT_IGNORE_OBJECT as _; + #[doc(alias = "GST_LOG_CONTEXT_IGNORE_FORMAT")] + const IGNORE_FORMAT = ffi::GST_LOG_CONTEXT_IGNORE_FORMAT as _; + #[doc(alias = "GST_LOG_CONTEXT_IGNORE_FILE")] + const IGNORE_FILE = ffi::GST_LOG_CONTEXT_IGNORE_FILE as _; + #[doc(alias = "GST_LOG_CONTEXT_USE_LINE_NUMBER")] + const USE_LINE_NUMBER = ffi::GST_LOG_CONTEXT_USE_LINE_NUMBER as _; + #[doc(alias = "GST_LOG_CONTEXT_USE_STRING_ARGS")] + const USE_STRING_ARGS = ffi::GST_LOG_CONTEXT_USE_STRING_ARGS as _; + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +#[doc(hidden)] +impl IntoGlib for LogContextHashFlags { + type GlibType = ffi::GstLogContextHashFlags; + + #[inline] + fn into_glib(self) -> ffi::GstLogContextHashFlags { + self.bits() + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +#[doc(hidden)] +impl FromGlib for LogContextHashFlags { + #[inline] + unsafe fn from_glib(value: ffi::GstLogContextHashFlags) -> Self { + skip_assert_initialized!(); + Self::from_bits_truncate(value) + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl StaticType for LogContextHashFlags { + #[inline] + #[doc(alias = "gst_log_context_hash_flags_get_type")] + fn static_type() -> glib::Type { + unsafe { from_glib(ffi::gst_log_context_hash_flags_get_type()) } + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl glib::HasParamSpec for LogContextHashFlags { + type ParamSpec = glib::ParamSpecFlags; + type SetValue = Self; + type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder; + + fn param_spec_builder() -> Self::BuilderFn { + Self::ParamSpec::builder + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl glib::value::ValueType for LogContextHashFlags { + type Type = Self; +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +unsafe impl<'a> glib::value::FromValue<'a> for LogContextHashFlags { + type Checker = glib::value::GenericValueTypeChecker; + + #[inline] + unsafe fn from_value(value: &'a glib::Value) -> Self { + skip_assert_initialized!(); + from_glib(glib::gobject_ffi::g_value_get_flags(value.to_glib_none().0)) + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl ToValue for LogContextHashFlags { + #[inline] + fn to_value(&self) -> glib::Value { + let mut value = glib::Value::for_value_type::(); + unsafe { + glib::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib()); + } + value + } + + #[inline] + fn value_type(&self) -> glib::Type { + Self::static_type() + } +} + +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +impl From for glib::Value { + #[inline] + fn from(v: LogContextHashFlags) -> Self { + skip_assert_initialized!(); + ToValue::to_value(&v) + } +} + bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[doc(alias = "GstMemoryFlags")] diff --git a/gstreamer/src/auto/mod.rs b/gstreamer/src/auto/mod.rs index 121839fac..d69c52360 100644 --- a/gstreamer/src/auto/mod.rs +++ b/gstreamer/src/auto/mod.rs @@ -165,6 +165,12 @@ pub use self::flags::EventTypeFlags; #[cfg(feature = "v1_20")] #[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))] pub use self::flags::GapFlags; +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +pub use self::flags::LogContextFlags; +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +pub use self::flags::LogContextHashFlags; pub use self::flags::MemoryFlags; pub use self::flags::MetaFlags; pub use self::flags::ObjectFlags; diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 985fa25d5..9194634ab 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -50,17 +50,24 @@ mod serde_macros; #[macro_use] pub mod log; +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +pub mod log_context; #[cfg(feature = "log")] pub use crate::log::DebugCategoryLogger; pub use crate::log::{ - DebugCategory, DebugLogFunction, DebugMessage, LoggedObject, CAT_BUFFER, CAT_BUFFER_LIST, - CAT_BUS, CAT_CALL_TRACE, CAT_CAPS, CAT_CLOCK, CAT_CONTEXT, CAT_DEFAULT, CAT_ELEMENT_PADS, - CAT_ERROR_SYSTEM, CAT_EVENT, CAT_GST_INIT, CAT_LOCKING, CAT_MEMORY, CAT_MESSAGE, CAT_META, - CAT_NEGOTIATION, CAT_PADS, CAT_PARAMS, CAT_PARENTAGE, CAT_PERFORMANCE, CAT_PIPELINE, - CAT_PLUGIN_INFO, CAT_PLUGIN_LOADING, CAT_PROBE, CAT_PROPERTIES, CAT_QOS, CAT_REFCOUNTING, - CAT_REGISTRY, CAT_RUST, CAT_SCHEDULING, CAT_SIGNAL, CAT_STATES, + DebugCategory, DebugLogFunction, DebugLogger, DebugMessage, LoggedObject, CAT_BUFFER, + CAT_BUFFER_LIST, CAT_BUS, CAT_CALL_TRACE, CAT_CAPS, CAT_CLOCK, CAT_CONTEXT, CAT_DEFAULT, + CAT_ELEMENT_PADS, CAT_ERROR_SYSTEM, CAT_EVENT, CAT_GST_INIT, CAT_LOCKING, CAT_MEMORY, + CAT_MESSAGE, CAT_META, CAT_NEGOTIATION, CAT_PADS, CAT_PARAMS, CAT_PARENTAGE, CAT_PERFORMANCE, + CAT_PIPELINE, CAT_PLUGIN_INFO, CAT_PLUGIN_LOADING, CAT_PROBE, CAT_PROPERTIES, CAT_QOS, + CAT_REFCOUNTING, CAT_REGISTRY, CAT_RUST, CAT_SCHEDULING, CAT_SIGNAL, CAT_STATES, }; +#[cfg(feature = "v1_28")] +#[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] +pub use crate::log_context::{LogContext, LogContextBuilder}; + #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "macos")] @@ -352,6 +359,7 @@ pub mod prelude { element::{ElementClassExt, ElementExtManual}, format::prelude::*, gobject::GObjectExtManualGst, + log::DebugLogger, memory::MemoryType, message::MessageErrorDomain, meta::{MetaAPI, MetaAPIExt, MetaTag}, diff --git a/gstreamer/src/log.rs b/gstreamer/src/log.rs index 8804da2eb..192d961e5 100644 --- a/gstreamer/src/log.rs +++ b/gstreamer/src/log.rs @@ -158,14 +158,6 @@ impl DebugCategory { } } - #[inline] - pub fn above_threshold(self, level: crate::DebugLevel) -> bool { - match self.0 { - Some(cat) => unsafe { cat.as_ref().threshold >= level.into_glib() }, - None => false, - } - } - #[doc(alias = "get_color")] #[doc(alias = "gst_debug_category_get_color")] #[inline] @@ -256,50 +248,6 @@ impl DebugCategory { // rustdoc-stripper-ignore-next /// Logs without checking the log level. - #[inline] - #[doc(alias = "gst_debug_log")] - pub fn log_unfiltered( - self, - obj: Option<&impl IsA>, - level: crate::DebugLevel, - file: &glib::GStr, - function: &str, - line: u32, - args: fmt::Arguments, - ) { - self.log_unfiltered_internal( - obj.map(|obj| obj.as_ref()), - level, - file, - function, - line, - args, - ) - } - - // rustdoc-stripper-ignore-next - /// Logs without checking the log level. - #[inline] - #[doc(alias = "gst_debug_log_literal")] - pub fn log_literal_unfiltered( - self, - obj: Option<&impl IsA>, - level: crate::DebugLevel, - file: &glib::GStr, - function: &str, - line: u32, - msg: &glib::GStr, - ) { - self.log_literal_unfiltered_internal( - obj.map(|obj| obj.as_ref()), - level, - file, - function, - line, - msg, - ) - } - #[inline(never)] fn log_unfiltered_internal( self, @@ -412,42 +360,6 @@ impl DebugCategory { self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg); } - #[cfg(feature = "v1_22")] - #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))] - // rustdoc-stripper-ignore-next - /// Logs without checking the log level. - #[inline] - #[doc(alias = "gst_debug_log_id")] - pub fn log_id_unfiltered( - self, - id: impl AsRef, - level: crate::DebugLevel, - file: &glib::GStr, - function: &str, - line: u32, - args: fmt::Arguments, - ) { - self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args) - } - - #[cfg(feature = "v1_22")] - #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))] - // rustdoc-stripper-ignore-next - /// Logs without checking the log level. - #[inline] - #[doc(alias = "gst_debug_log_id_literal")] - pub fn log_id_literal_unfiltered( - self, - id: impl AsRef, - level: crate::DebugLevel, - file: &glib::GStr, - function: &str, - line: u32, - msg: &glib::GStr, - ) { - self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg) - } - #[cfg(feature = "v1_22")] #[inline(never)] fn log_id_unfiltered_internal( @@ -542,6 +454,95 @@ impl DebugCategory { } } +impl DebugLogger for DebugCategory { + #[inline] + fn above_threshold(&self, level: crate::DebugLevel) -> bool { + match self.0 { + Some(cat) => unsafe { cat.as_ref().threshold >= level.into_glib() }, + None => false, + } + } + + // rustdoc-stripper-ignore-next + /// Logs without checking the log level. + #[inline] + #[doc(alias = "gst_debug_log")] + fn log_unfiltered( + &self, + obj: Option<&impl IsA>, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + self.log_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + args, + ) + } + + #[doc(alias = "gst_debug_log_literal")] + fn log_literal_unfiltered( + &self, + obj: Option<&impl IsA>, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + self.log_literal_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + msg, + ) + } + + #[cfg(feature = "v1_22")] + #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))] + // rustdoc-stripper-ignore-next + /// Logs without checking the log level. + #[inline] + #[doc(alias = "gst_debug_log_id_literal")] + fn log_id_literal_unfiltered( + &self, + id: impl AsRef, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg) + } + + #[cfg(feature = "v1_22")] + #[cfg_attr(docsrs, doc(cfg(feature = "v1_22")))] + // rustdoc-stripper-ignore-next + /// Logs without checking the log level. + #[inline] + #[doc(alias = "gst_debug_log_id")] + fn log_id_unfiltered( + &self, + id: impl AsRef, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args) + } +} + unsafe impl Sync for DebugCategory {} unsafe impl Send for DebugCategory {} @@ -621,144 +622,192 @@ declare_debug_category_from_name!(CAT_META, "GST_META"); declare_debug_category_from_name!(CAT_LOCKING, "GST_LOCKING"); declare_debug_category_from_name!(CAT_CONTEXT, "GST_CONTEXT"); +pub trait DebugLogger { + fn above_threshold(&self, level: DebugLevel) -> bool; + + fn log_unfiltered( + &self, + obj: Option<&impl IsA>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ); + + fn log_literal_unfiltered( + &self, + obj: Option<&impl IsA>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ); + + #[cfg(feature = "v1_22")] + fn log_id_unfiltered( + &self, + id: impl AsRef, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ); + + #[cfg(feature = "v1_22")] + fn log_id_literal_unfiltered( + &self, + id: impl AsRef, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ); +} + #[macro_export] macro_rules! error( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Error, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Error, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Error, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Error, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Error, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Error, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Error, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Error, $($args)*) }}; ); #[macro_export] macro_rules! warning( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Warning, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Warning, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Warning, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Warning, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Warning, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Warning, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Warning, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Warning, $($args)*) }}; ); #[macro_export] macro_rules! fixme( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Fixme, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Fixme, $($args)*) }}; ); #[macro_export] macro_rules! info( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Info, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Info, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Info, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Info, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Info, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Info, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Info, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Info, $($args)*) }}; ); #[macro_export] macro_rules! debug( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Debug, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Debug, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Debug, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Debug, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Debug, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Debug, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Debug, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Debug, $($args)*) }}; ); #[macro_export] macro_rules! log( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Log, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Log, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Log, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Log, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Log, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Log, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Log, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Log, $($args)*) }}; ); #[macro_export] macro_rules! trace( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Trace, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Trace, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Trace, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Trace, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Trace, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Trace, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Trace, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Trace, $($args)*) }}; ); #[macro_export] macro_rules! memdump( - ($cat:expr, obj = $obj:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, obj = $obj, $($args)*) + ($logger:expr, obj = $obj:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, obj = $obj, $($args)*) }}; - ($cat:expr, imp = $imp:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, imp = $imp, $($args)*) + ($logger:expr, imp = $imp:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, imp = $imp, $($args)*) }}; - ($cat:expr, id = $id:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, id = $id, $($args)*) + ($logger:expr, id = $id:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, id = $id, $($args)*) }}; - ($cat:expr, $($args:tt)*) => { { - $crate::log_with_level!($cat, $crate::DebugLevel::Memdump, $($args)*) + ($logger:expr, $($args:tt)*) => { { + $crate::log_with_level!($logger, $crate::DebugLevel::Memdump, $($args)*) }}; ); #[macro_export] macro_rules! log_with_level( - ($cat:expr, $level:expr, obj = $obj:expr, $msg:literal) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, obj = $obj:expr, $msg:literal) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] #[allow(clippy::redundant_closure_call)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { use $crate::glib::prelude::Cast; // FIXME: Once there's a function_name! macro that returns a string literal we can @@ -773,8 +822,7 @@ macro_rules! log_with_level( // be assigned to a variable (|args: std::fmt::Arguments| { if args.as_str().is_some() { - $crate::DebugCategory::log_literal_unfiltered( - cat, + logger.log_literal_unfiltered( Some(obj), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -783,8 +831,7 @@ macro_rules! log_with_level( $crate::glib::gstr!($msg), ) } else { - $crate::DebugCategory::log_unfiltered( - cat, + logger.log_unfiltered( Some(obj), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -796,13 +843,15 @@ macro_rules! log_with_level( })(format_args!($msg)) } }}; - ($cat:expr, $level:expr, obj = $obj:expr, $($args:tt)*) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, obj = $obj:expr, $($args:tt)*) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { use $crate::glib::prelude::Cast; // FIXME: Once there's a function_name! macro that returns a string literal we can @@ -810,25 +859,26 @@ macro_rules! log_with_level( let obj = &$obj; let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() }; - $crate::DebugCategory::log_unfiltered( - cat, - Some(obj), - $level, - unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, - $crate::glib::function_name!(), - line!(), - format_args!($($args)*), - ) + logger.log_unfiltered( + Some(obj), + $level, + unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, + $crate::glib::function_name!(), + line!(), + format_args!($($args)*), + ) } }}; - ($cat:expr, $level:expr, imp = $imp:expr, $msg:literal) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, imp = $imp:expr, $msg:literal) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] #[allow(clippy::redundant_closure_call)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { use $crate::glib::prelude::Cast; // FIXME: Once there's a function_name! macro that returns a string literal we can @@ -843,8 +893,7 @@ macro_rules! log_with_level( // be assigned to a variable (|args: std::fmt::Arguments| { if args.as_str().is_some() { - $crate::DebugCategory::log_literal_unfiltered( - cat, + logger.log_literal_unfiltered( Some(obj), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -853,8 +902,7 @@ macro_rules! log_with_level( $crate::glib::gstr!($msg), ) } else { - $crate::DebugCategory::log_unfiltered( - cat, + logger.log_unfiltered( Some(obj), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -866,13 +914,15 @@ macro_rules! log_with_level( })(format_args!($msg)) } }}; - ($cat:expr, $level:expr, imp = $imp:expr, $($args:tt)*) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, imp = $imp:expr, $($args:tt)*) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { use $crate::glib::prelude::Cast; // FIXME: Once there's a function_name! macro that returns a string literal we can @@ -880,25 +930,26 @@ macro_rules! log_with_level( let obj = $imp.obj(); let obj = unsafe { obj.unsafe_cast_ref::<$crate::glib::Object>() }; - $crate::DebugCategory::log_unfiltered( - cat, - Some(obj), - $level, - unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, - $crate::glib::function_name!(), - line!(), - format_args!($($args)*), - ) + logger.log_unfiltered( + Some(obj), + $level, + unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, + $crate::glib::function_name!(), + line!(), + format_args!($($args)*), + ) } }}; - ($cat:expr, $level:expr, id = $id:literal, $msg:literal) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, id = $id:literal, $msg:literal) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] #[allow(clippy::redundant_closure_call)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward @@ -909,8 +960,7 @@ macro_rules! log_with_level( // be assigned to a variable (|args: std::fmt::Arguments| { if args.as_str().is_some() { - $crate::DebugCategory::log_id_literal_unfiltered( - cat, + logger.log_id_literal_unfiltered( $crate::glib::gstr!($id), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -919,8 +969,7 @@ macro_rules! log_with_level( $crate::glib::gstr!($msg), ) } else { - $crate::DebugCategory::log_id_unfiltered( - cat, + logger.log_id_unfiltered( $crate::glib::gstr!($id), $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -932,35 +981,38 @@ macro_rules! log_with_level( })(format_args!($msg)) } }}; - ($cat:expr, $level:expr, id = $id:literal, $($args:tt)*) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, id = $id:literal, $($args:tt)*) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward - $crate::DebugCategory::log_id_unfiltered( - cat, - $crate::glib::gstr!($id), - $level, - unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, - $crate::glib::function_name!(), - line!(), - format_args!($($args)*), - ) + logger.log_id_unfiltered( + $crate::glib::gstr!($id), + $level, + unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, + $crate::glib::function_name!(), + line!(), + format_args!($($args)*), + ) } }}; - ($cat:expr, $level:expr, id = $id:expr, $msg:literal) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, id = $id:expr, $msg:literal) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] #[allow(clippy::redundant_closure_call)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward @@ -971,8 +1023,7 @@ macro_rules! log_with_level( // be assigned to a variable (|args: std::fmt::Arguments| { if args.as_str().is_some() { - $crate::DebugCategory::log_id_literal_unfiltered( - cat, + logger.log_id_literal_unfiltered( $id, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -981,8 +1032,7 @@ macro_rules! log_with_level( $crate::glib::gstr!($msg), ) } else { - $crate::DebugCategory::log_id_unfiltered( - cat, + logger.log_id_unfiltered( $id, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -994,18 +1044,19 @@ macro_rules! log_with_level( })(format_args!($msg)) } }}; - ($cat:expr, $level:expr, id = $id:expr, $($args:tt)*) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, id = $id:expr, $($args:tt)*) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward - $crate::DebugCategory::log_id_unfiltered( - cat, + logger.log_id_unfiltered( $id, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -1015,14 +1066,16 @@ macro_rules! log_with_level( ) } }}; - ($cat:expr, $level:expr, $msg:literal) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, $msg:literal) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] #[allow(clippy::redundant_closure_call)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward @@ -1033,8 +1086,7 @@ macro_rules! log_with_level( // be assigned to a variable (|args: std::fmt::Arguments| { if args.as_str().is_some() { - $crate::DebugCategory::log_literal_unfiltered( - cat, + logger.log_literal_unfiltered( None as Option<&$crate::glib::Object>, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -1043,8 +1095,7 @@ macro_rules! log_with_level( $crate::glib::gstr!($msg), ) } else { - $crate::DebugCategory::log_unfiltered( - cat, + logger.log_unfiltered( None as Option<&$crate::glib::Object>, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, @@ -1056,18 +1107,19 @@ macro_rules! log_with_level( })(format_args!($msg)) } }}; - ($cat:expr, $level:expr, $($args:tt)*) => { { - let cat = $cat.clone(); + ($logger:expr, $level:expr, $($args:tt)*) => { { + #[allow(unused_imports)] + use $crate::log::DebugLogger; + let logger = &$logger; // Check the log level before using `format_args!` otherwise // formatted arguments are evaluated even if we end up not logging. #[allow(unused_unsafe)] - if cat.above_threshold($level) { + if logger.above_threshold($level) { // FIXME: Once there's a function_name! macro that returns a string literal we can // directly pass it as `&GStr` forward - $crate::DebugCategory::log_unfiltered( - cat, + logger.log_unfiltered( None as Option<&$crate::glib::Object>, $level, unsafe { $crate::glib::GStr::from_utf8_with_nul_unchecked(concat!(file!(), "\0").as_bytes()) }, diff --git a/gstreamer/src/log_context.rs b/gstreamer/src/log_context.rs new file mode 100644 index 000000000..41db80836 --- /dev/null +++ b/gstreamer/src/log_context.rs @@ -0,0 +1,649 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use std::{fmt, io::Write, ptr}; + +use glib::{prelude::*, translate::*}; + +use crate::log::DebugLogger; +use crate::{ffi, ClockTime, DebugCategory, DebugLevel, LogContextFlags, LogContextHashFlags}; + +#[derive(Debug)] +#[doc(alias = "GstLogContext")] +#[repr(transparent)] +pub struct LogContext(ptr::NonNull); + +impl LogContext { + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_get_category")] + #[doc(alias = "get_category")] + #[inline] + pub fn category(&self) -> DebugCategory { + unsafe { from_glib_none(ffi::gst_log_context_get_category(self.0.as_ptr())) } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_reset")] + #[inline] + pub fn reset(&self) { + unsafe { + ffi::gst_log_context_reset(self.0.as_ptr()); + } + } + + #[inline] + pub fn as_ptr(&self) -> *mut ffi::GstLogContext { + self.0.as_ptr() + } + + // rustdoc-stripper-ignore-next + /// Logs without checking the log level. + #[inline(never)] + fn log_unfiltered_internal( + &self, + obj: Option<&glib::Object>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + let mut w = smallvec::SmallVec::<[u8; 256]>::new(); + + // Can't really happen but better safe than sorry + if Write::write_fmt(&mut w, args).is_err() { + return; + } + w.push(0); + + self.log_literal_unfiltered_internal(obj, level, file, function, line, unsafe { + glib::GStr::from_utf8_with_nul_unchecked(&w) + }); + } + + #[inline(never)] + fn log_literal_unfiltered_internal( + &self, + obj: Option<&glib::Object>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + let obj_ptr = match obj { + Some(obj) => obj.as_ptr(), + None => ptr::null_mut(), + }; + + function.run_with_gstr(|function| unsafe { + ffi::gst_debug_log_literal_with_context( + self.0.as_ptr(), + level.into_glib(), + file.as_ptr(), + function.as_ptr(), + line as i32, + obj_ptr, + msg.as_ptr(), + ); + }); + } + + #[inline(never)] + fn log_id_unfiltered_internal( + &self, + id: &glib::GStr, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + let mut w = smallvec::SmallVec::<[u8; 256]>::new(); + + // Can't really happen but better safe than sorry + if Write::write_fmt(&mut w, args).is_err() { + return; + } + w.push(0); + + self.log_id_literal_unfiltered_internal(id, level, file, function, line, unsafe { + glib::GStr::from_utf8_with_nul_unchecked(&w) + }); + } + + #[inline(never)] + fn log_id_literal_unfiltered_internal( + &self, + id: &glib::GStr, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + function.run_with_gstr(|function| unsafe { + ffi::gst_debug_log_id_literal_with_context( + self.0.as_ptr(), + level.into_glib(), + file.as_ptr(), + function.as_ptr(), + line as i32, + id.as_ptr(), + msg.as_ptr(), + ); + }); + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_debug_log_with_context")] + pub fn log( + &self, + obj: Option<&impl IsA>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + if !self.above_threshold(level) { + return; + } + + self.log_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + args, + ) + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_debug_log_literal_with_context")] + pub fn log_literal( + &self, + obj: Option<&impl IsA>, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + if !self.above_threshold(level) { + return; + } + + self.log_literal_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + msg, + ) + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_debug_log_id_with_context")] + pub fn log_id( + &self, + id: impl AsRef, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: fmt::Arguments, + ) { + if !self.above_threshold(level) { + return; + } + + self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args); + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_debug_log_id_literal_with_context")] + pub fn log_id_literal( + &self, + id: impl AsRef, + level: DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + if !self.above_threshold(level) { + return; + } + + self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg); + } +} + +impl Drop for LogContext { + fn drop(&mut self) { + unsafe { + ffi::gst_log_context_free(self.0.as_ptr()); + } + } +} + +unsafe impl Send for LogContext {} +unsafe impl Sync for LogContext {} + +impl crate::log::DebugLogger for LogContext { + #[inline] + fn above_threshold(&self, level: crate::DebugLevel) -> bool { + self.category().above_threshold(level) + } + + #[inline] + fn log_unfiltered( + &self, + obj: Option<&impl IsA>, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: std::fmt::Arguments, + ) { + self.log_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + args, + ) + } + + #[inline] + fn log_literal_unfiltered( + &self, + obj: Option<&impl IsA>, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + self.log_literal_unfiltered_internal( + obj.map(|obj| obj.as_ref()), + level, + file, + function, + line, + msg, + ) + } + + #[inline] + fn log_id_unfiltered( + &self, + id: impl AsRef, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + args: std::fmt::Arguments, + ) { + self.log_id_unfiltered_internal(id.as_ref(), level, file, function, line, args) + } + + // rustdoc-stripper-ignore-next + /// Logs without checking the log level. + #[inline] + fn log_id_literal_unfiltered( + &self, + id: impl AsRef, + level: crate::DebugLevel, + file: &glib::GStr, + function: &str, + line: u32, + msg: &glib::GStr, + ) { + self.log_id_literal_unfiltered_internal(id.as_ref(), level, file, function, line, msg) + } +} + +#[derive(Debug)] +#[doc(alias = "GstLogContextBuilder")] +#[repr(transparent)] +pub struct LogContextBuilder(ptr::NonNull); + +impl LogContextBuilder { + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_builder_new")] + pub fn new(category: DebugCategory, flags: LogContextFlags) -> Self { + skip_assert_initialized!(); + unsafe { + let ptr = ffi::gst_log_context_builder_new(category.as_ptr(), flags.into_glib()); + LogContextBuilder(ptr::NonNull::new_unchecked(ptr)) + } + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_builder_set_hash_flags")] + pub fn hash_flags(self, flags: LogContextHashFlags) -> Self { + unsafe { + ffi::gst_log_context_builder_set_hash_flags(self.0.as_ptr(), flags.into_glib()); + } + self + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_builder_set_category")] + pub fn category(self, category: DebugCategory) -> Self { + unsafe { + ffi::gst_log_context_builder_set_category(self.0.as_ptr(), category.as_ptr()); + } + self + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_builder_set_interval")] + pub fn interval(self, interval: impl Into>) -> Self { + unsafe { + ffi::gst_log_context_builder_set_interval(self.0.as_ptr(), interval.into().into_glib()); + } + self + } + + #[cfg_attr(docsrs, doc(cfg(feature = "v1_28")))] + #[doc(alias = "gst_log_context_builder_build")] + pub fn build(self) -> LogContext { + unsafe { + let ptr = ffi::gst_log_context_builder_build(self.0.as_ptr()); + LogContext(ptr::NonNull::new_unchecked(ptr)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{log::DebugLogger, DebugLevel, LogContextFlags, LogContextHashFlags}; + + #[test] + fn log_context_builder_basic() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-log-context", + crate::DebugColorFlags::empty(), + Some("Test log context category"), + ); + + // Test basic builder pattern + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + assert_eq!(context.category(), cat); + } + + #[test] + fn log_context_builder_with_flags() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-log-context-flags", + crate::DebugColorFlags::empty(), + Some("Test log context with flags"), + ); + + // Test builder with various configuration options + let context = LogContextBuilder::new(cat, LogContextFlags::THROTTLE) + .hash_flags(LogContextHashFlags::USE_LINE_NUMBER) + .interval(Some(crate::ClockTime::from_seconds(1))) + .build(); + + assert_eq!(context.category(), cat); + } + + #[test] + fn log_context_trait_implementation() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-trait-impl", + crate::DebugColorFlags::empty(), + Some("Test trait implementation"), + ); + + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + // Test that LogContext implements DebugLogger trait + assert_eq!( + context.above_threshold(DebugLevel::Error), + cat.above_threshold(DebugLevel::Error) + ); + assert_eq!( + context.above_threshold(DebugLevel::Debug), + cat.above_threshold(DebugLevel::Debug) + ); + } + + #[test] + fn log_context_with_macros() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-macro-usage", + crate::DebugColorFlags::empty(), + Some("Test macro usage"), + ); + cat.set_threshold(DebugLevel::Trace); + + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + // Test that LogContext works with all the logging macros + crate::error!(context, "error message"); + crate::error!(&context, "error message"); + crate::warning!(context, "warning message"); + crate::warning!(&context, "warning message"); + crate::info!(context, "info message"); + crate::info!(&context, "info message"); + crate::debug!(context, "debug message"); + crate::debug!(&context, "debug message"); + crate::trace!(context, "trace message"); + crate::trace!(&context, "trace message"); + + // Test with object + let obj = crate::Bin::with_name("test-bin"); + crate::error!(context, obj = &obj, "error with object"); + crate::warning!(context, obj = &obj, "warning with object"); + + // Test with formatting + let value = 42; + crate::info!(context, "formatted message: {}", value); + crate::debug!(context, obj = &obj, "formatted with obj: {}", value); + } + + #[test] + fn log_context_interchangeable_with_category() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-interchangeable", + crate::DebugColorFlags::empty(), + Some("Test interchangeable usage"), + ); + cat.set_threshold(DebugLevel::Info); + + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + // Test that we can use both category and context in the same way + let test_message = "test message"; + + // These should both work identically + crate::info!(cat, "{}", test_message); + crate::info!(&cat, "{}", test_message); + crate::info!(context, "{}", test_message); + crate::info!(&context, "{}", test_message); + + // With objects too + let obj = crate::Bin::with_name("test-bin-2"); + crate::info!(cat, obj = &obj, "{}", test_message); + crate::info!(context, obj = &obj, "{}", test_message); + } + + #[test] + fn static_log_context() { + crate::init().unwrap(); + + // Create a static category first + static TEST_CATEGORY: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + crate::DebugCategory::new( + "test-static-context", + crate::DebugColorFlags::empty(), + Some("Test static context"), + ) + }); + + // Create static context directly with LazyLock + static TEST_CONTEXT: std::sync::LazyLock = std::sync::LazyLock::new(|| { + LogContextBuilder::new(*TEST_CATEGORY, LogContextFlags::empty()).build() + }); + + // Use the static context + crate::info!(TEST_CONTEXT, "message from static context"); + + assert_eq!(TEST_CONTEXT.category(), *TEST_CATEGORY); + } + + #[test] + fn static_log_context_with_advanced_options() { + crate::init().unwrap(); + + // Create a static category first + static ADVANCED_CATEGORY: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + crate::DebugCategory::new( + "test-static-advanced", + crate::DebugColorFlags::empty(), + Some("Test static context advanced"), + ) + }); + + // Create static context with advanced options using LazyLock + static ADVANCED_CONTEXT: std::sync::LazyLock = std::sync::LazyLock::new(|| { + LogContextBuilder::new(*ADVANCED_CATEGORY, LogContextFlags::THROTTLE) + .hash_flags(LogContextHashFlags::USE_LINE_NUMBER) + .interval(Some(crate::ClockTime::from_seconds(2))) + .build() + }); + + crate::debug!(ADVANCED_CONTEXT, "advanced static context message"); + assert_eq!(ADVANCED_CONTEXT.category(), *ADVANCED_CATEGORY); + } + + #[test] + fn log_context_with_id_logging() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-id-logging", + crate::DebugColorFlags::empty(), + Some("Test ID logging"), + ); + cat.set_threshold(DebugLevel::Trace); + + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + // Test ID-based logging with LogContext + crate::trace!(context, id = "test-id-123", "message with ID"); + crate::debug!( + context, + id = "test-id-456", + "another message with ID: {}", + 42 + ); + } + + #[test] + fn log_context_memory_safety() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-memory-safety", + crate::DebugColorFlags::empty(), + Some("Test memory safety"), + ); + + // Test that LogContext can be safely dropped + { + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + crate::info!(context, "message before drop"); + } // context is dropped here + + // Create another context to ensure no memory issues + let context2 = LogContextBuilder::new(cat, LogContextFlags::THROTTLE).build(); + crate::info!(context2, "message from second context"); + } + + #[test] + fn log_context_category_consistency() { + crate::init().unwrap(); + + let cat1 = crate::DebugCategory::new( + "test-consistency-1", + crate::DebugColorFlags::empty(), + Some("Test consistency 1"), + ); + + let cat2 = crate::DebugCategory::new( + "test-consistency-2", + crate::DebugColorFlags::empty(), + Some("Test consistency 2"), + ); + + let context1 = LogContextBuilder::new(cat1, LogContextFlags::empty()).build(); + let context2 = LogContextBuilder::new(cat2, LogContextFlags::empty()).build(); + + // Verify that contexts maintain their respective categories + assert_eq!(context1.category(), cat1); + assert_eq!(context2.category(), cat2); + assert_ne!(context1.category(), cat2); + assert_ne!(context2.category(), cat1); + } + + #[test] + fn log_context_threshold_behavior() { + crate::init().unwrap(); + + let cat = crate::DebugCategory::new( + "test-threshold", + crate::DebugColorFlags::empty(), + Some("Test threshold behavior"), + ); + + let context = LogContextBuilder::new(cat, LogContextFlags::empty()).build(); + + // Test threshold behavior matches between category and context + cat.set_threshold(DebugLevel::Warning); + + assert!(context.above_threshold(DebugLevel::Error)); + assert!(context.above_threshold(DebugLevel::Warning)); + assert!(!context.above_threshold(DebugLevel::Info)); + assert!(!context.above_threshold(DebugLevel::Debug)); + + // Same as category + assert_eq!( + context.above_threshold(DebugLevel::Error), + cat.above_threshold(DebugLevel::Error) + ); + assert_eq!( + context.above_threshold(DebugLevel::Warning), + cat.above_threshold(DebugLevel::Warning) + ); + assert_eq!( + context.above_threshold(DebugLevel::Info), + cat.above_threshold(DebugLevel::Info) + ); + assert_eq!( + context.above_threshold(DebugLevel::Debug), + cat.above_threshold(DebugLevel::Debug) + ); + } +}