From 2d2ded555ec4441081ad8b1fb1699d52f1bf58a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Laignel?= Date: Sun, 6 Oct 2024 22:01:23 +0200 Subject: [PATCH] gst: structure: deprecate Quarks and use IdStr Update Structure API: * Quarks API are deprecated. Methods which were internally calling quarks methods now call C string based methods. * Added new `IdStr` methods. See also: * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7432 * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7613 * https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7644 Part-of: --- gstreamer-audio/src/caps.rs | 32 +- gstreamer-pbutils/src/element_properties.rs | 54 +- gstreamer-rtsp-server/src/rtsp_token.rs | 27 + gstreamer-video/src/caps.rs | 32 +- gstreamer/src/caps.rs | 30 +- gstreamer/src/structure.rs | 1227 ++++++++++++++++++- gstreamer/src/structure_serde.rs | 2 +- gstreamer/src/value.rs | 122 ++ 8 files changed, 1459 insertions(+), 67 deletions(-) diff --git a/gstreamer-audio/src/caps.rs b/gstreamer-audio/src/caps.rs index 62ebe050d..33cfe131d 100644 --- a/gstreamer-audio/src/caps.rs +++ b/gstreamer-audio/src/caps.rs @@ -1,6 +1,6 @@ use std::ops::{Bound::*, RangeBounds}; -use gst::Caps; +use gst::{Caps, IdStr}; use glib::IntoGStr; @@ -422,6 +422,36 @@ impl AudioCapsBuilder { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_static( + self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + Self { + builder: self.builder.field_with_static(name, value), + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_id( + self, + name: impl AsRef, + value: impl Into + Send, + ) -> Self { + Self { + builder: self.builder.field_with_id(name, value), + } + } + gst::impl_builder_gvalue_extra_setters!(field); #[must_use] diff --git a/gstreamer-pbutils/src/element_properties.rs b/gstreamer-pbutils/src/element_properties.rs index 7ef9dd525..b52554752 100644 --- a/gstreamer-pbutils/src/element_properties.rs +++ b/gstreamer-pbutils/src/element_properties.rs @@ -1,6 +1,6 @@ use std::ops::{Deref, DerefMut}; -use gst::prelude::*; +use gst::{prelude::*, IdStr}; // rustdoc-stripper-ignore-next /// Wrapper around `gst::Structure` for `element-properties` @@ -150,6 +150,32 @@ impl ElementPropertiesGeneralBuilder { self } + // rustdoc-stripper-ignore-next + /// Sets property `property_name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `property_name`. + pub fn field_with_static( + mut self, + property_name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + self.structure.set_with_static(property_name, value); + self + } + + // rustdoc-stripper-ignore-next + /// Sets property `property_name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `property_name`. + pub fn field_with_id( + mut self, + property_name: impl AsRef, + value: impl Into + Send, + ) -> Self { + self.structure.set_with_id(property_name, value); + self + } + gst::impl_builder_gvalue_extra_setters!(field); pub fn field_value(mut self, property_name: &str, value: glib::SendValue) -> Self { @@ -288,6 +314,32 @@ impl ElementPropertiesMapItemBuilder { self } + // rustdoc-stripper-ignore-next + /// Sets property `property_name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `property_name`. + pub fn field_with_static( + mut self, + property_name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + self.structure.set_with_static(property_name, value); + self + } + + // rustdoc-stripper-ignore-next + /// Sets property `property_name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `property_name`. + pub fn field_with_id( + mut self, + property_name: impl AsRef, + value: impl Into + Send, + ) -> Self { + self.structure.set_with_id(property_name, value); + self + } + gst::impl_builder_gvalue_extra_setters!(field); pub fn field_value(mut self, property_name: &str, value: glib::SendValue) -> Self { diff --git a/gstreamer-rtsp-server/src/rtsp_token.rs b/gstreamer-rtsp-server/src/rtsp_token.rs index ff632ecbf..fc71e6590 100644 --- a/gstreamer-rtsp-server/src/rtsp_token.rs +++ b/gstreamer-rtsp-server/src/rtsp_token.rs @@ -4,6 +4,7 @@ use std::fmt; use crate::ffi; use glib::{translate::*, SendValue}; +use gst::IdStr; gst::mini_object_wrapper!(RTSPToken, RTSPTokenRef, ffi::GstRTSPToken, || { ffi::gst_rtsp_token_get_type() @@ -108,6 +109,32 @@ impl Builder { self } + pub fn field_with_static( + mut self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + self.token + .get_mut() + .unwrap() + .structure_mut() + .set_with_static(name, value); + self + } + + pub fn field_with_id( + mut self, + name: impl AsRef, + value: impl Into + Send, + ) -> Self { + self.token + .get_mut() + .unwrap() + .structure_mut() + .set_with_id(name, value); + self + } + gst::impl_builder_gvalue_extra_setters!(field); #[must_use = "Building the structure without using it has no effect"] diff --git a/gstreamer-video/src/caps.rs b/gstreamer-video/src/caps.rs index 0985c312b..2ff222c9d 100644 --- a/gstreamer-video/src/caps.rs +++ b/gstreamer-video/src/caps.rs @@ -1,7 +1,7 @@ use std::ops::{Bound::*, RangeBounds}; use glib::translate::*; -use gst::Caps; +use gst::{Caps, IdStr}; use crate::VideoFormat; @@ -530,6 +530,36 @@ impl VideoCapsBuilder { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_static( + self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + Self { + builder: self.builder.field_with_static(name, value), + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_id( + self, + name: impl AsRef, + value: impl Into + Send, + ) -> Self { + Self { + builder: self.builder.field_with_id(name, value), + } + } + gst::impl_builder_gvalue_extra_setters!(field); #[must_use] diff --git a/gstreamer/src/caps.rs b/gstreamer/src/caps.rs index 82b5ce70e..1baa2acd3 100644 --- a/gstreamer/src/caps.rs +++ b/gstreamer/src/caps.rs @@ -8,7 +8,7 @@ use glib::{ value::{SendValue, ToSendValue}, }; -use crate::{caps_features::*, ffi, structure::*, CapsIntersectMode}; +use crate::{caps_features::*, ffi, structure::*, CapsIntersectMode, IdStr}; mini_object_wrapper!(Caps, CapsRef, ffi::GstCaps, || { ffi::gst_caps_get_type() }); @@ -1116,6 +1116,34 @@ impl Builder { self } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_static( + mut self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + self.s.set_with_static(name, value); + self + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_id( + mut self, + name: impl AsRef, + value: impl Into + Send, + ) -> Self { + self.s.set_with_id(name, value); + self + } + impl_builder_gvalue_extra_setters!(field); #[must_use = "Building the caps without using them has no effect"] diff --git a/gstreamer/src/structure.rs b/gstreamer/src/structure.rs index c38ea15d2..14d3190a8 100644 --- a/gstreamer/src/structure.rs +++ b/gstreamer/src/structure.rs @@ -9,36 +9,44 @@ use std::{ ptr, str, }; +use cfg_if::cfg_if; use glib::{ prelude::*, translate::*, value::{FromValue, SendValue, Value}, - IntoGStr, + GStr, IntoGStr, }; -use crate::{ffi, Fraction}; +use crate::{ffi, Fraction, IdStr}; #[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)] pub enum GetError { #[error("GetError: Structure field with name {name} not found")] - FieldNotFound { name: &'static str }, + FieldNotFound { name: IdStr }, #[error("GetError: Structure field with name {name} not retrieved")] ValueGetError { - name: &'static str, + name: IdStr, #[source] error: E, }, } impl GetError { - fn new_field_not_found(name: &'static str) -> Self { + #[inline] + fn new_field_not_found(name: impl AsRef) -> Self { skip_assert_initialized!(); - GetError::FieldNotFound { name } + GetError::FieldNotFound { + name: name.as_ref().clone(), + } } - fn from_value_get_error(name: &'static str, error: E) -> Self { + #[inline] + fn from_value_get_error(name: impl AsRef, error: E) -> Self { skip_assert_initialized!(); - GetError::ValueGetError { name, error } + GetError::ValueGetError { + name: name.as_ref().clone(), + error, + } } } @@ -55,6 +63,18 @@ impl Structure { Builder::new(name) } + #[doc(alias = "gst_structure_new_static_str_empty")] + pub fn builder_static(name: impl AsRef + 'static) -> Builder { + skip_assert_initialized!(); + Builder::from_static(name) + } + + #[doc(alias = "gst_structure_new_id_str")] + pub fn builder_from_id(name: impl AsRef) -> Builder { + skip_assert_initialized!(); + Builder::from_id(name) + } + #[doc(alias = "gst_structure_new_empty")] pub fn new_empty(name: impl IntoGStr) -> Structure { assert_initialized_main_thread!(); @@ -65,6 +85,40 @@ impl Structure { } } + #[doc(alias = "gst_structure_new_static_str_empty")] + pub fn new_empty_from_static(name: impl AsRef + 'static) -> Structure { + assert_initialized_main_thread!(); + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + let ptr = + ffi::gst_structure_new_static_str_empty(name.as_ref().as_ptr()); + } else { + let ptr = ffi::gst_structure_new_empty(name.as_ref().as_ptr()); + } + } + debug_assert!(!ptr.is_null()); + Structure(ptr::NonNull::new_unchecked(ptr)) + } + } + + #[doc(alias = "gst_structure_new_id_str_empty")] + pub fn new_empty_from_id(name: impl AsRef) -> Structure { + assert_initialized_main_thread!(); + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + let ptr = ffi::gst_structure_new_id_str_empty(name.as_ref().as_ptr()); + } else { + let ptr = ffi::gst_structure_new_empty(name.as_ref().as_gstr().as_ptr()); + } + } + + debug_assert!(!ptr.is_null()); + Structure(ptr::NonNull::new_unchecked(ptr)) + } + } + #[allow(clippy::should_implement_trait)] pub fn from_iter( name: impl IntoGStr, @@ -78,6 +132,34 @@ impl Structure { structure } + + #[allow(clippy::should_implement_trait)] + pub fn from_iter_with_static( + name: impl AsRef + 'static, + iter: impl IntoIterator + 'static, SendValue)>, + ) -> Structure { + skip_assert_initialized!(); + let mut structure = Structure::new_empty_from_static(name); + + iter.into_iter() + .for_each(|(f, v)| structure.set_value_with_static(f, v)); + + structure + } + + #[allow(clippy::should_implement_trait)] + pub fn from_iter_with_id( + name: impl AsRef, + iter: impl IntoIterator, SendValue)>, + ) -> Structure { + skip_assert_initialized!(); + let mut structure = Structure::new_empty_from_id(name); + + iter.into_iter() + .for_each(|(f, v)| structure.set_value_with_id(f, v)); + + structure + } } impl IntoGlibPtr<*mut ffi::GstStructure> for Structure { @@ -421,8 +503,31 @@ impl StructureRef { name: impl IntoGStr, ) -> Result>::Checker as glib::value::ValueTypeChecker>::Error>> { - let name = glib::Quark::from_str(name); - self.get_by_quark(name) + name.run_with_gstr(|name| { + self.value(name) + .map_err(|err| match err { + GetError::FieldNotFound { name } => GetError::FieldNotFound { name }, + _ => unreachable!(), + })? + .get() + .map_err(|err| GetError::from_value_get_error(IdStr::from(name), err)) + }) + } + + #[doc(alias = "gst_structure_id_str_get")] + #[inline] + pub fn get_by_id<'a, T: FromValue<'a>>( + &'a self, + name: impl AsRef, + ) -> Result>::Checker as glib::value::ValueTypeChecker>::Error>> + { + self.value_by_id(name.as_ref()) + .map_err(|err| match err { + GetError::FieldNotFound { name } => GetError::FieldNotFound { name }, + _ => unreachable!(), + })? + .get() + .map_err(|err| GetError::from_value_get_error(name, err)) } #[doc(alias = "gst_structure_get")] @@ -433,8 +538,28 @@ impl StructureRef { Option, GetError<<>::Checker as glib::value::ValueTypeChecker>::Error>, > { - let name = glib::Quark::from_str(name); - self.get_optional_by_quark(name) + name.run_with_gstr(|name| { + self.value(name) + .ok() + .map(|v| v.get()) + .transpose() + .map_err(|err| GetError::from_value_get_error(IdStr::from(name), err)) + }) + } + + #[doc(alias = "gst_structure_id_str_get")] + pub fn get_optional_by_id<'a, T: FromValue<'a>>( + &'a self, + name: impl AsRef, + ) -> Result< + Option, + GetError<<>::Checker as glib::value::ValueTypeChecker>::Error>, + > { + self.value_by_id(name.as_ref()) + .ok() + .map(|v| v.get()) + .transpose() + .map_err(|err| GetError::from_value_get_error(name, err)) } #[doc(alias = "get_value")] @@ -443,10 +568,43 @@ impl StructureRef { &self, name: impl IntoGStr, ) -> Result<&SendValue, GetError> { - let name = glib::Quark::from_str(name); - self.value_by_quark(name) + unsafe { + name.run_with_gstr(|name| { + let value = ffi::gst_structure_get_value(&self.0, name.as_ptr()); + + if value.is_null() { + return Err(GetError::new_field_not_found(IdStr::from(name))); + } + + Ok(&*(value as *const SendValue)) + }) + } } + #[doc(alias = "gst_structure_id_str_get_value")] + pub fn value_by_id( + &self, + name: impl AsRef, + ) -> Result<&SendValue, GetError> { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + let value = ffi::gst_structure_id_str_get_value(&self.0, name.as_ref().as_ptr()); + } else { + let value = ffi::gst_structure_get_value(&self.0, name.as_ref().as_gstr().as_ptr()); + } + } + + if value.is_null() { + return Err(GetError::new_field_not_found(name)); + } + + Ok(&*(value as *const SendValue)) + } + } + + #[deprecated = "use `get_by_id()` instead"] + #[allow(deprecated)] #[doc(alias = "gst_structure_id_get")] pub fn get_by_quark<'a, T: FromValue<'a>>( &'a self, @@ -459,9 +617,11 @@ impl StructureRef { _ => unreachable!(), })? .get() - .map_err(|err| GetError::from_value_get_error(name.as_str(), err)) + .map_err(|err| GetError::from_value_get_error(IdStr::from(name.as_str()), err)) } + #[deprecated = "use `get_optional_by_id()` instead"] + #[allow(deprecated)] #[doc(alias = "gst_structure_id_get")] pub fn get_optional_by_quark<'a, T: FromValue<'a>>( &'a self, @@ -474,9 +634,10 @@ impl StructureRef { .ok() .map(|v| v.get()) .transpose() - .map_err(|err| GetError::from_value_get_error(name.as_str(), err)) + .map_err(|err| GetError::from_value_get_error(IdStr::from(name.as_str()), err)) } + #[deprecated = "use `value_by_id()` instead"] #[doc(alias = "gst_structure_id_get_value")] pub fn value_by_quark( &self, @@ -486,7 +647,7 @@ impl StructureRef { let value = ffi::gst_structure_id_get_value(&self.0, name.into_glib()); if value.is_null() { - return Err(GetError::new_field_not_found(name.as_str())); + return Err(GetError::new_field_not_found(IdStr::from(name.as_str()))); } Ok(&*(value as *const SendValue)) @@ -503,6 +664,30 @@ impl StructureRef { self.set_value(name, value); } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[doc(alias = "gst_structure_set_static_str")] + pub fn set_with_static( + &mut self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) { + let value = glib::SendValue::from_owned(value); + self.set_value_with_static(name, value); + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[doc(alias = "gst_structure_id_str_set")] + pub fn set_with_id(&mut self, name: impl AsRef, value: impl Into + Send) { + let value = glib::SendValue::from_owned(value); + self.set_value_with_id(name, value); + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. /// @@ -520,6 +705,40 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_set_static_str")] + pub fn set_with_static_if( + &mut self, + name: impl AsRef + 'static, + value: impl Into + Send, + predicate: bool, + ) { + if predicate { + self.set_with_static(name, value); + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_id_str_set")] + pub fn set_with_id_if( + &mut self, + name: impl AsRef, + value: impl Into + Send, + predicate: bool, + ) { + if predicate { + self.set_with_id(name, value); + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given inner value if `value` is `Some`. /// @@ -535,6 +754,36 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_set_static_str")] + pub fn set_with_static_if_some( + &mut self, + name: impl AsRef + 'static, + value: Option + Send>, + ) { + if let Some(value) = value { + self.set_with_static(name, value); + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_id_str_set")] + pub fn set_with_id_if_some( + &mut self, + name: impl AsRef, + value: Option + Send>, + ) { + if let Some(value) = value { + self.set_with_id(name, value); + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. /// @@ -549,6 +798,36 @@ impl StructureRef { self.set(name, V::from_iter(iter)); } + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn set_with_static_from_iter< + V: ValueType + Into + FromIterator + Send, + >( + &mut self, + name: impl AsRef + 'static, + iter: impl IntoIterator, + ) { + let iter = iter.into_iter().map(|item| item.to_send_value()); + self.set_with_static(name, V::from_iter(iter)); + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn set_with_id_from_iter + FromIterator + Send>( + &mut self, + name: impl AsRef, + iter: impl IntoIterator, + ) { + let iter = iter.into_iter().map(|item| item.to_send_value()); + self.set_with_id(name, V::from_iter(iter)); + } + // rustdoc-stripper-ignore-next /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, /// if `iter` is not empty. @@ -567,6 +846,44 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, + /// if `iter` is not empty. + /// + /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged. + #[inline] + pub fn set_with_static_if_not_empty< + V: ValueType + Into + FromIterator + Send, + >( + &mut self, + name: impl AsRef + 'static, + iter: impl IntoIterator, + ) { + let mut iter = iter.into_iter().peekable(); + if iter.peek().is_some() { + let iter = iter.map(|item| item.to_send_value()); + self.set_with_static(name, V::from_iter(iter)); + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, + /// if `iter` is not empty. + /// + /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged. + #[inline] + pub fn set_with_id_if_not_empty + FromIterator + Send>( + &mut self, + name: impl AsRef, + iter: impl IntoIterator, + ) { + let mut iter = iter.into_iter().peekable(); + if iter.peek().is_some() { + let iter = iter.map(|item| item.to_send_value()); + self.set_with_id(name, V::from_iter(iter)); + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given value `value`. /// @@ -580,6 +897,56 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[doc(alias = "gst_structure_set_value_static_str")] + pub fn set_value_with_static(&mut self, name: impl AsRef + 'static, value: SendValue) { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + ffi::gst_structure_take_value_static_str( + &mut self.0, + name.as_ref().as_ptr(), + &mut value.into_raw(), + ) + } else { + ffi::gst_structure_take_value( + &mut self.0, + name.as_ref().as_ptr(), + &mut value.into_raw(), + ) + } + } + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[doc(alias = "gst_structure_id_str_set_value")] + pub fn set_value_with_id(&mut self, name: impl AsRef, value: SendValue) { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + ffi::gst_structure_id_str_take_value( + &mut self.0, + name.as_ref().as_ptr(), + &mut value.into_raw(), + ) + } else { + ffi::gst_structure_take_value( + &mut self.0, + name.as_ref().as_gstr().as_ptr(), + &mut value.into_raw(), + ) + } + } + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. /// @@ -592,6 +959,40 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_set_value_static_str")] + pub fn set_value_with_static_if( + &mut self, + name: impl AsRef + 'static, + value: SendValue, + predicate: bool, + ) { + if predicate { + self.set_value_with_static(name, value); + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given `value` if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_id_str_set_value")] + pub fn set_value_with_id_if( + &mut self, + name: impl AsRef, + value: SendValue, + predicate: bool, + ) { + if predicate { + self.set_value_with_id(name, value); + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given inner value if `value` is `Some`. /// @@ -603,12 +1004,42 @@ impl StructureRef { } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_set_value_static_str")] + pub fn set_value_with_static_if_some( + &mut self, + name: impl AsRef + 'static, + value: Option, + ) { + if let Some(value) = value { + self.set_value_with_static(name, value); + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[doc(alias = "gst_structure_id_str_set_value")] + pub fn set_value_with_id_if_some(&mut self, name: impl AsRef, value: Option) { + if let Some(value) = value { + self.set_value_with_id(name, value); + } + } + + #[deprecated = "use `set_by_id()` instead"] + #[allow(deprecated)] #[doc(alias = "gst_structure_id_set")] pub fn set_by_quark(&mut self, name: glib::Quark, value: impl Into + Send) { let value = glib::SendValue::from_owned(value); self.set_value_by_quark(name, value); } + #[deprecated = "use `set_by_id_if_some()` instead"] + #[allow(deprecated)] #[doc(alias = "gst_structure_id_set")] pub fn set_by_quark_if_some( &mut self, @@ -620,6 +1051,7 @@ impl StructureRef { } } + #[deprecated = "use `set_by_id_value()` instead"] #[doc(alias = "gst_structure_id_set_value")] pub fn set_value_by_quark(&mut self, name: glib::Quark, value: SendValue) { unsafe { @@ -627,6 +1059,8 @@ impl StructureRef { } } + #[deprecated = "use `set_by_id_value_if_some()` instead"] + #[allow(deprecated)] #[doc(alias = "gst_structure_id_set_value")] pub fn set_value_by_quark_if_some(&mut self, name: glib::Quark, value: Option) { if let Some(value) = value { @@ -636,14 +1070,18 @@ impl StructureRef { #[doc(alias = "get_name")] #[doc(alias = "gst_structure_get_name")] - pub fn name<'a>(&self) -> &'a glib::GStr { - unsafe { - let name = ffi::gst_structure_get_name(&self.0); - // Ensure the name is static whatever the GStreamer version being used. - glib::GStr::from_ptr(glib::ffi::g_intern_string(name)) - } + pub fn name(&self) -> &glib::GStr { + unsafe { glib::GStr::from_ptr(ffi::gst_structure_get_name(&self.0)) } } + #[cfg(feature = "v1_26")] + #[doc(alias = "get_name")] + #[doc(alias = "gst_structure_get_name_id_str")] + pub fn name_id(&self) -> &IdStr { + unsafe { &*(ffi::gst_structure_get_name_id_str(&self.0) as *const crate::IdStr) } + } + + #[deprecated = "use `name()` instead, or `name_id()` with feature v1_26"] #[doc(alias = "gst_structure_get_name_id")] pub fn name_quark(&self) -> glib::Quark { unsafe { from_glib(ffi::gst_structure_get_name_id(&self.0)) } @@ -656,6 +1094,38 @@ impl StructureRef { } } + #[doc(alias = "gst_structure_set_name_static_str")] + pub fn set_name_from_static(&mut self, name: impl AsRef + 'static) { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + ffi::gst_structure_set_name_static_str( + &mut self.0, + name.as_ref().as_ptr(), + ) + } else { + ffi::gst_structure_set_name(&mut self.0, name.as_ref().as_ptr()) + } + } + } + } + + #[doc(alias = "gst_structure_set_name_id_str")] + pub fn set_name_from_id(&mut self, name: impl AsRef) { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + ffi::gst_structure_set_name_id_str( + &mut self.0, + name.as_ref().as_ptr(), + ) + } else { + ffi::gst_structure_set_name(&mut self.0, name.as_ref().as_gstr().as_ptr()) + } + } + } + } + #[doc(alias = "gst_structure_set_name")] pub fn set_name_if_some(&mut self, name: Option) { if let Some(name) = name { @@ -663,6 +1133,20 @@ impl StructureRef { } } + #[doc(alias = "gst_structure_set_name_static_str")] + pub fn set_name_from_static_if_some(&mut self, name: Option + 'static>) { + if let Some(name) = name { + self.set_name_from_static(name); + } + } + + #[doc(alias = "gst_structure_set_name_id_str")] + pub fn set_name_from_id_if_some(&mut self, name: Option>) { + if let Some(name) = name { + self.set_name_from_id(name); + } + } + #[doc(alias = "gst_structure_has_name")] pub fn has_name(&self, name: &str) -> bool { self.name() == name @@ -677,6 +1161,25 @@ impl StructureRef { } } + #[doc(alias = "gst_structure_id_str_has_field")] + pub fn has_field_by_id(&self, field: impl AsRef) -> bool { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + from_glib(ffi::gst_structure_id_str_has_field( + &self.0, + field.as_ref().as_ptr(), + )) + } else { + from_glib(ffi::gst_structure_has_field( + &self.0, + field.as_ref().as_gstr().as_ptr(), + )) + } + } + } + } + #[doc(alias = "gst_structure_has_field_typed")] pub fn has_field_with_type(&self, field: impl IntoGStr, type_: glib::Type) -> bool { unsafe { @@ -690,11 +1193,34 @@ impl StructureRef { } } + #[doc(alias = "gst_structure_id_str_has_field_typed")] + pub fn has_field_with_type_by_id(&self, field: impl AsRef, type_: glib::Type) -> bool { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + from_glib(ffi::gst_structure_id_str_has_field_typed( + &self.0, + field.as_ref().as_ptr(), + type_.into_glib(), + )) + } else { + from_glib(ffi::gst_structure_has_field_typed( + &self.0, + field.as_ref().as_gstr().as_ptr(), + type_.into_glib(), + )) + } + } + } + } + + #[deprecated = "use `has_field_by_id()`"] #[doc(alias = "gst_structure_id_has_field")] pub fn has_field_by_quark(&self, field: glib::Quark) -> bool { unsafe { from_glib(ffi::gst_structure_id_has_field(&self.0, field.into_glib())) } } + #[deprecated = "use `has_field_with_type_by_id()`"] #[doc(alias = "gst_structure_id_has_field_typed")] pub fn has_field_with_type_by_quark(&self, field: glib::Quark, type_: glib::Type) -> bool { unsafe { @@ -722,6 +1248,26 @@ impl StructureRef { } } + #[doc(alias = "gst_structure_id_str_remove_field")] + pub fn remove_field_by_id(&mut self, field: impl AsRef) { + unsafe { + cfg_if! { + if #[cfg(feature = "v1_26")] { + ffi::gst_structure_id_str_remove_field(&mut self.0, field.as_ref().as_ptr()) + } else { + ffi::gst_structure_remove_field(&mut self.0, field.as_ref().as_gstr().as_ptr()) + } + } + } + } + + #[doc(alias = "gst_structure_id_str_remove_fields")] + pub fn remove_field_by_ids(&mut self, fields: impl IntoIterator>) { + for f in fields.into_iter() { + self.remove_field_by_id(f) + } + } + #[doc(alias = "gst_structure_remove_all_fields")] pub fn remove_all_fields(&mut self) { unsafe { @@ -737,9 +1283,19 @@ impl StructureRef { Iter::new(self) } + #[cfg(feature = "v1_26")] + pub fn field_ids(&self) -> FieldIdIterator { + FieldIdIterator::new(self) + } + + #[cfg(feature = "v1_26")] + pub fn id_iter(&self) -> IdIter { + IdIter::new(self) + } + #[doc(alias = "get_nth_field_name")] #[doc(alias = "gst_structure_nth_field_name")] - pub fn nth_field_name<'a>(&self, idx: usize) -> Option<&'a glib::GStr> { + pub fn nth_field_name(&self, idx: usize) -> Option<&glib::GStr> { if idx >= self.n_fields() { return None; } @@ -748,8 +1304,23 @@ impl StructureRef { let field_name = ffi::gst_structure_nth_field_name(&self.0, idx as u32); debug_assert!(!field_name.is_null()); - // Ensure the name is static whatever the GStreamer version being used. - Some(glib::GStr::from_ptr(glib::ffi::g_intern_string(field_name))) + Some(glib::GStr::from_ptr(field_name)) + } + } + + #[cfg(feature = "v1_26")] + #[doc(alias = "get_nth_field_name")] + #[doc(alias = "gst_structure_id_str_nth_field_name")] + pub fn nth_field_by_id(&self, idx: usize) -> Option<&IdStr> { + if idx >= self.n_fields() { + return None; + } + + unsafe { + let field_name = ffi::gst_structure_id_str_nth_field_name(&self.0, idx as u32); + debug_assert!(!field_name.is_null()); + + Some(&*(field_name as *const crate::IdStr)) } } @@ -898,6 +1469,7 @@ impl StructureRef { } } + #[deprecated = "Use `iter()` instead, or `id_iter()` with feature v1_26"] #[doc(alias = "gst_structure_foreach")] pub fn foreach std::ops::ControlFlow<()>>( &self, @@ -925,11 +1497,103 @@ impl StructureRef { } } + #[cfg(feature = "v1_26")] + // rustdoc-stripper-ignore-next + /// Executes the provided `func` on each field, possibly modifying the value. + #[doc(alias = "gst_structure_map_in_place_id_str")] + pub fn map_in_place_by_id std::ops::ControlFlow<()>>( + &mut self, + mut func: F, + ) { + unsafe { + unsafe extern "C" fn trampoline< + F: FnMut(&IdStr, &mut glib::Value) -> std::ops::ControlFlow<()>, + >( + fieldname: *const ffi::GstIdStr, + value: *mut glib::gobject_ffi::GValue, + user_data: glib::ffi::gpointer, + ) -> glib::ffi::gboolean { + let func = &mut *(user_data as *mut F); + let res = func( + &*(fieldname as *const IdStr), + &mut *(value as *mut glib::Value), + ); + + matches!(res, std::ops::ControlFlow::Continue(_)).into_glib() + } + let func = &mut func as *mut F; + let _ = ffi::gst_structure_map_in_place_id_str( + self.as_mut_ptr(), + Some(trampoline::), + func as glib::ffi::gpointer, + ); + } + } + + #[cfg(feature = "v1_26")] + // rustdoc-stripper-ignore-next + /// Executes the provided `func` on each field with an owned value. + /// + /// * If `func` returns `Some(value)`, the field's value is replaced with + /// `value`. + /// * If `func` returns `None`, the field is removed. + #[doc(alias = "gst_structure_filter_and_map_in_place_id_str")] + pub fn filter_map_in_place_by_id Option>( + &mut self, + mut func: F, + ) { + unsafe { + unsafe extern "C" fn trampoline< + F: FnMut(&IdStr, glib::Value) -> Option, + >( + fieldname: *const ffi::GstIdStr, + value: *mut glib::gobject_ffi::GValue, + user_data: glib::ffi::gpointer, + ) -> glib::ffi::gboolean { + let func = &mut *(user_data as *mut F); + + let v = mem::replace( + &mut *(value as *mut glib::Value), + glib::Value::uninitialized(), + ); + match func(&*(fieldname as *const IdStr), v) { + None => glib::ffi::GFALSE, + Some(v) => { + *value = v.into_raw(); + glib::ffi::GTRUE + } + } + } + + let func = &mut func as *mut F; + ffi::gst_structure_filter_and_map_in_place_id_str( + self.as_mut_ptr(), + Some(trampoline::), + func as glib::ffi::gpointer, + ); + } + } + + // rustdoc-stripper-ignore-next + /// Executes the provided `func` on each field, possibly modifying the value, + /// as long as `ControlFlow::Continue(())` is returned. + /// + /// Using `Quark`s is deprecated, however this method is kept because there + /// are no other means to achieve this pre-GStreamer-1.26. With feature v1_26, + /// use [map_in_place_by_id] instead. + /// + /// # Returns: + /// + /// * `ControlFlow::Continue(())` if `func` returned this for all fields, + /// * `ControlFlow::Break(())` otherwise. + /// + /// [map_in_place_by_id]: crate::StructureRef::map_in_place_by_id + #[cfg_attr(feature = "v1_26", deprecated = "use `map_in_place_by_id()` instead")] #[doc(alias = "gst_structure_map_in_place")] pub fn map_in_place std::ops::ControlFlow<()>>( &mut self, mut func: F, - ) -> bool { + ) -> std::ops::ControlFlow<()> { unsafe { unsafe extern "C" fn trampoline< F: FnMut(glib::Quark, &mut glib::Value) -> std::ops::ControlFlow<()>, @@ -944,14 +1608,34 @@ impl StructureRef { matches!(res, std::ops::ControlFlow::Continue(_)).into_glib() } let func = &mut func as *mut F; - from_glib(ffi::gst_structure_map_in_place( + if from_glib(ffi::gst_structure_map_in_place( self.as_mut_ptr(), Some(trampoline::), func as glib::ffi::gpointer, - )) + )) { + std::ops::ControlFlow::Continue(()) + } else { + std::ops::ControlFlow::Break(()) + } } } + // rustdoc-stripper-ignore-next + /// Executes the provided `func` on each field with an owned value. + /// + /// * If `func` returns `Some(value)`, the field's value is replaced with + /// `value`. + /// * If `func` returns `None`, the field is removed. + /// + /// Using `Quark`s is deprecated, however this method is kept because there + /// are no other means to achieve this pre-GStreamer-1.26. With feature v1_26, + /// use [filter_map_in_place_by_id] instead. + /// + /// [filter_map_in_place_by_id]: crate::StructureRef::filter_map_in_place_by_id + #[cfg_attr( + feature = "v1_26", + deprecated = "use `filter_map_in_place_by_id()` instead" + )] #[doc(alias = "gst_structure_filter_and_map_in_place")] pub fn filter_map_in_place Option>( &mut self, @@ -1097,15 +1781,13 @@ impl<'a> FieldIterator<'a> { } impl<'a> Iterator for FieldIterator<'a> { - type Item = &'static glib::GStr; + type Item = &'a glib::GStr; fn next(&mut self) -> Option { if self.idx >= self.n_fields { return None; } - // Safety: nth_field_name() ensures static lifetime for the returned string, - // whatever the GStreamer version being used. let field_name = self.structure.nth_field_name(self.idx).unwrap(); self.idx += 1; @@ -1136,10 +1818,71 @@ impl<'a> ExactSizeIterator for FieldIterator<'a> {} impl<'a> std::iter::FusedIterator for FieldIterator<'a> {} +#[cfg(feature = "v1_26")] +#[derive(Debug)] +pub struct FieldIdIterator<'a> { + structure: &'a StructureRef, + idx: usize, + n_fields: usize, +} + +#[cfg(feature = "v1_26")] +impl<'a> FieldIdIterator<'a> { + fn new(structure: &'a StructureRef) -> FieldIdIterator<'a> { + skip_assert_initialized!(); + let n_fields = structure.n_fields(); + + FieldIdIterator { + structure, + idx: 0, + n_fields, + } + } +} + +#[cfg(feature = "v1_26")] +impl<'a> Iterator for FieldIdIterator<'a> { + type Item = &'a IdStr; + + fn next(&mut self) -> Option { + if self.idx >= self.n_fields { + return None; + } + + let field_name = self.structure.nth_field_by_id(self.idx).unwrap(); + self.idx += 1; + + Some(field_name) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.n_fields - self.idx; + + (remaining, Some(remaining)) + } +} + +#[cfg(feature = "v1_26")] +impl<'a> DoubleEndedIterator for FieldIdIterator<'a> { + fn next_back(&mut self) -> Option { + if self.idx == self.n_fields { + return None; + } + + self.n_fields -= 1; + // Safety: nth_field_name() ensures static lifetime for the returned string, + // whatever the GStreamer version being used. + Some(self.structure.nth_field_by_id(self.n_fields).unwrap()) + } +} + +#[cfg(feature = "v1_26")] +impl<'a> ExactSizeIterator for FieldIdIterator<'a> {} +#[cfg(feature = "v1_26")] +impl<'a> std::iter::FusedIterator for FieldIdIterator<'a> {} + #[derive(Debug)] pub struct Iter<'a> { - // Safety: FieldIterator ensures static lifetime for the returned Item, - // whatever the GStreamer version being used. iter: FieldIterator<'a>, } @@ -1153,7 +1896,7 @@ impl<'a> Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = (&'static glib::GStr, &'a SendValue); + type Item = (&'a glib::GStr, &'a SendValue); fn next(&mut self) -> Option { let f = self.iter.next()?; @@ -1201,9 +1944,77 @@ impl<'a> ExactSizeIterator for Iter<'a> {} impl<'a> std::iter::FusedIterator for Iter<'a> {} +#[cfg(feature = "v1_26")] +#[derive(Debug)] +pub struct IdIter<'a> { + iter: FieldIdIterator<'a>, +} + +#[cfg(feature = "v1_26")] +impl<'a> IdIter<'a> { + fn new(structure: &'a StructureRef) -> IdIter<'a> { + skip_assert_initialized!(); + IdIter { + iter: FieldIdIterator::new(structure), + } + } +} + +#[cfg(feature = "v1_26")] +impl<'a> Iterator for IdIter<'a> { + type Item = (&'a IdStr, &'a SendValue); + + fn next(&mut self) -> Option { + let f = self.iter.next()?; + let v = self.iter.structure.value_by_id(f); + Some((f, v.unwrap())) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn count(self) -> usize { + self.iter.count() + } + + fn nth(&mut self, n: usize) -> Option { + let f = self.iter.nth(n)?; + let v = self.iter.structure.value_by_id(f); + Some((f, v.unwrap())) + } + + fn last(self) -> Option { + let structure = self.iter.structure; + let f = self.iter.last()?; + let v = structure.value_by_id(f); + Some((f, v.unwrap())) + } +} + +#[cfg(feature = "v1_26")] +impl<'a> DoubleEndedIterator for IdIter<'a> { + fn next_back(&mut self) -> Option { + let f = self.iter.next_back()?; + let v = self.iter.structure.value_by_id(f); + Some((f, v.unwrap())) + } + + fn nth_back(&mut self, n: usize) -> Option { + let f = self.iter.nth_back(n)?; + let v = self.iter.structure.value_by_id(f); + Some((f, v.unwrap())) + } +} + +#[cfg(feature = "v1_26")] +impl<'a> ExactSizeIterator for IdIter<'a> {} +#[cfg(feature = "v1_26")] +impl<'a> std::iter::FusedIterator for IdIter<'a> {} + impl<'a> IntoIterator for &'a StructureRef { type IntoIter = Iter<'a>; - type Item = (&'static glib::GStr, &'a SendValue); + type Item = (&'a glib::GStr, &'a SendValue); fn into_iter(self) -> Self::IntoIter { self.iter() @@ -1234,7 +2045,24 @@ impl std::iter::Extend<(glib::GString, SendValue)> for StructureRef { } } +impl<'a> std::iter::Extend<(&'a IdStr, SendValue)> for StructureRef { + #[allow(deprecated)] + fn extend>(&mut self, iter: T) { + iter.into_iter() + .for_each(|(f, v)| self.set_value_with_id(f, v)); + } +} + +impl std::iter::Extend<(IdStr, SendValue)> for StructureRef { + #[allow(deprecated)] + fn extend>(&mut self, iter: T) { + iter.into_iter() + .for_each(|(f, v)| self.set_value_with_id(f, v)); + } +} + impl std::iter::Extend<(glib::Quark, SendValue)> for StructureRef { + #[allow(deprecated)] fn extend>(&mut self, iter: T) { iter.into_iter() .for_each(|(f, v)| self.set_value_by_quark(f, v)); @@ -1255,6 +2083,20 @@ impl Builder { } } + fn from_static(name: impl AsRef + 'static) -> Self { + skip_assert_initialized!(); + Builder { + s: Structure::new_empty_from_static(name), + } + } + + pub fn from_id(name: impl AsRef) -> Builder { + skip_assert_initialized!(); + Builder { + s: Structure::new_empty_from_id(name), + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given value `value`. /// @@ -1265,6 +2107,34 @@ impl Builder { self } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_static( + mut self, + name: impl AsRef + 'static, + value: impl Into + Send, + ) -> Self { + self.s.set_with_static(name, value); + self + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given value `value`. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_id( + mut self, + name: impl AsRef, + value: impl Into + Send, + ) -> Self { + self.s.set_with_id(name, value); + self + } + impl_builder_gvalue_extra_setters!(field); #[must_use = "Building the structure without using it has no effect"] @@ -1276,6 +2146,7 @@ impl Builder { #[cfg(test)] mod tests { use super::*; + use glib::gstr; #[test] fn new_set_get() { @@ -1305,22 +2176,25 @@ mod tests { assert_eq!( s.get::("f2"), Err(GetError::from_value_get_error( - "f2", + idstr!("f2"), value::ValueTypeMismatchError::new(Type::STRING, Type::I32), )) ); assert_eq!( s.get::("f3"), Err(GetError::from_value_get_error( - "f3", + idstr!("f3"), value::ValueTypeMismatchError::new(Type::I32, Type::BOOL), )) ); assert_eq!( s.get::<&str>("f4"), - Err(GetError::new_field_not_found("f4")) + Err(GetError::new_field_not_found(idstr!("f4"))) + ); + assert_eq!( + s.get::("f4"), + Err(GetError::new_field_not_found(idstr!("f4"))) ); - assert_eq!(s.get::("f4"), Err(GetError::new_field_not_found("f4"))); assert_eq!( s.fields().collect::>(), @@ -1365,6 +2239,106 @@ mod tests { assert_eq!(s, s3); } + #[test] + fn new_set_get_static() { + use glib::{value, Type}; + + crate::init().unwrap(); + + let mut s = Structure::new_empty_from_static(gstr!("test")); + assert_eq!(s.name(), "test"); + + static F1: &GStr = gstr!("f1"); + static F2: &GStr = gstr!("f2"); + static F3: &GStr = gstr!("f3"); + + s.set_with_static(F1, "abc"); + s.set_with_static_if(F2, String::from("bcd"), true); + s.set_with_static_if(F3, "not_set", false); + + assert_eq!(s.get::<&str>(F1), Ok("abc")); + assert_eq!(s.get::>(F2), Ok(Some("bcd"))); + assert_eq!(s.get_optional::<&str>(F1), Ok(Some("abc"))); + assert_eq!(s.get_optional::<&str>(F3), Ok(None)); + + assert_eq!( + s.get::(F2), + Err(GetError::from_value_get_error( + idstr!("f2"), + value::ValueTypeMismatchError::new(Type::STRING, Type::I32), + )) + ); + assert_eq!( + s.get::<&str>(F3), + Err(GetError::new_field_not_found(idstr!("f3"))) + ); + + let s2 = Structure::builder("test") + .field_with_static(F1, "abc") + .field_with_static(F2, String::from("bcd")) + .build(); + assert_eq!(s, s2); + + let mut s3 = Structure::new_empty("test"); + + s3.set_with_static_if_some(F1, Some("abc")); + s3.set_with_static_if_some(F2, Some(String::from("bcd"))); + + assert_eq!(s, s3); + } + + #[test] + fn new_set_get_id_str() { + use glib::{value, Type}; + + crate::init().unwrap(); + + let mut s = Structure::new_empty_from_id(idstr!("test")); + assert_eq!(s.name(), "test"); + #[cfg(feature = "v1_26")] + assert_eq!(s.name_id(), "test"); + + let f1 = idstr!("f1"); + let f2 = idstr!("f2"); + let f3 = idstr!("f3"); + + s.set_with_id(&f1, "abc"); + s.set_with_id_if(&f2, String::from("bcd"), true); + s.set_with_id_if(&f3, "not_set", false); + + assert_eq!(s.get_by_id::<&str>(&f1), Ok("abc")); + assert_eq!(s.get_by_id::<&str>(f1.clone()), Ok("abc")); + assert_eq!(s.get_by_id::>(&f2), Ok(Some("bcd"))); + assert_eq!(s.get_by_id::>(f2.clone()), Ok(Some("bcd"))); + assert_eq!(s.get_optional_by_id::<&str>(&f1), Ok(Some("abc"))); + assert_eq!(s.get_optional_by_id::<&str>(&f3), Ok(None)); + + assert_eq!( + s.get_by_id::(&f2), + Err(GetError::from_value_get_error( + f2.clone(), + value::ValueTypeMismatchError::new(Type::STRING, Type::I32), + )) + ); + assert_eq!( + s.get_by_id::<&str>(&f3), + Err(GetError::new_field_not_found(f3.clone())) + ); + + let s2 = Structure::builder("test") + .field_with_id(&f1, "abc") + .field_with_id(&f2, String::from("bcd")) + .build(); + assert_eq!(s, s2); + + let mut s3 = Structure::new_empty("test"); + + s3.set_with_id_if_some(f1, Some("abc")); + s3.set_with_id_if_some(f2, Some(String::from("bcd"))); + + assert_eq!(s, s3); + } + #[test] fn test_string_conversion() { crate::init().unwrap(); @@ -1396,8 +2370,8 @@ mod tests { let s = Structure::builder("test") .field("f1", "abc") - .field("f2", String::from("bcd")) - .field("f3", 123i32) + .field_with_static(gstr!("f2"), String::from("bcd")) + .field_with_id(idstr!("f3"), 123i32) .build(); let s2 = Structure::from_iter( @@ -1410,7 +2384,7 @@ mod tests { assert_eq!(s2.name(), "test"); assert_eq!(s2.get::<&str>("f1"), Ok("abc")); assert!(s2.get::<&str>("f2").is_err()); - assert!(s2.get::<&str>("f3").is_err()); + assert!(s2.get_by_id::<&str>(idstr!("f3")).is_err()); } #[test] @@ -1436,9 +2410,12 @@ mod tests { fn builder_field_from_iter() { crate::init().unwrap(); + static SLIST: &GStr = gstr!("slist"); + let ilist = idstr!("ilist"); let s = Structure::builder("test") .field_from_iter::("array", [&1, &2, &3]) - .field_from_iter::("list", [&4, &5, &6]) + .field_with_static_from_iter::(SLIST, [&4, &5, &6]) + .field_with_id_from_iter::(&ilist, [&7, &8, &9]) .build(); assert!(s .get::("array") @@ -1447,28 +2424,43 @@ mod tests { .map(|val| val.get::().unwrap()) .eq([1, 2, 3])); assert!(s - .get::("list") + .get::("slist") .unwrap() .iter() .map(|val| val.get::().unwrap()) .eq([4, 5, 6])); + assert!(s + .get_by_id::(&ilist) + .unwrap() + .iter() + .map(|val| val.get::().unwrap()) + .eq([7, 8, 9])); let array = Vec::::new(); let s = Structure::builder("test") .field_from_iter::("array", &array) - .field_from_iter::("list", &array) + .field_with_static_from_iter::(SLIST, &array) + .field_with_id_from_iter::(&ilist, &array) .build(); assert!(s.get::("array").unwrap().as_ref().is_empty()); - assert!(s.get::("list").unwrap().as_ref().is_empty()); + assert!(s.get::(SLIST).unwrap().as_ref().is_empty()); + assert!(s + .get_by_id::(ilist) + .unwrap() + .as_ref() + .is_empty()); } #[test] fn builder_field_if_not_empty() { crate::init().unwrap(); - let s = Structure::builder("test") + static SLIST: &GStr = gstr!("slist"); + let ilist = idstr!("ilist"); + let s = Structure::builder_from_id(idstr!("test")) .field_if_not_empty::("array", [&1, &2, &3]) - .field_if_not_empty::("list", [&4, &5, &6]) + .field_with_static_if_not_empty::(SLIST, [&4, &5, &6]) + .field_with_id_if_not_empty::(&ilist, [&7, &8, &9]) .build(); assert!(s .get::("array") @@ -1477,35 +2469,146 @@ mod tests { .map(|val| val.get::().unwrap()) .eq([1, 2, 3])); assert!(s - .get::("list") + .get::("slist") .unwrap() .iter() .map(|val| val.get::().unwrap()) .eq([4, 5, 6])); + assert!(s + .get_by_id::(&ilist) + .unwrap() + .iter() + .map(|val| val.get::().unwrap()) + .eq([7, 8, 9])); let array = Vec::::new(); let s = Structure::builder("test") .field_if_not_empty::("array", &array) - .field_if_not_empty::("list", &array) + .field_with_static_if_not_empty::(SLIST, &array) + .field_with_id_if_not_empty::(ilist, &array) .build(); assert!(!s.has_field("array")); - assert!(!s.has_field("list")); + assert!(!s.has_field("slist")); + assert!(!s.has_field("ilist")); } #[test] - fn test_name_and_field_name_lt() { + fn nth_field_remove_field() { crate::init().unwrap(); - let (name, field_name) = { - let s = Structure::builder("name") - .field("field0", "val0") - .field("field1", "val1") - .build(); + let f3 = idstr!("f3"); + let f5 = idstr!("f5"); + let f8 = idstr!("f8"); + let mut s = Structure::builder("test") + .field("f1", "abc") + .field("f2", "bcd") + .field_with_id(&f3, "cde") + .field("f4", "def") + .field_with_id(&f5, "efg") + .field("f6", "fgh") + .field("f7", "ghi") + .field_with_id(&f8, "hij") + .build(); - (s.name(), s.nth_field_name(0).unwrap()) - }; + assert_eq!(s.iter().next().unwrap().0, "f1"); + assert_eq!( + s.fields().collect::>(), + vec!["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8"] + ); + assert!(s.has_field("f8")); + assert_eq!(s.nth_field_name(7), Some(gstr!("f8"))); + assert!(s.nth_field_name(8).is_none()); - assert_eq!(name, "name"); - assert_eq!(field_name, "field0"); + #[cfg(feature = "v1_26")] + assert_eq!(s.id_iter().next().unwrap().0, "f1"); + #[cfg(feature = "v1_26")] + assert_eq!( + s.field_ids().collect::>(), + vec!["f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8"] + ); + #[cfg(feature = "v1_26")] + assert!(s.has_field_by_id(&f8)); + #[cfg(feature = "v1_26")] + assert_eq!(s.nth_field_by_id(7), Some(&f8)); + #[cfg(feature = "v1_26")] + assert!(s.nth_field_by_id(8).is_none()); + + assert_eq!(s.nth_field_name(1), Some(gstr!("f2"))); + s.remove_field("f2"); + assert_eq!(s.nth_field_name(1), Some(gstr!("f3"))); + assert!(s.nth_field_name(7).is_none()); + assert_eq!( + s.fields().collect::>(), + vec!["f1", "f3", "f4", "f5", "f6", "f7", "f8"] + ); + + assert_eq!(s.nth_field_name(1), Some(gstr!("f3"))); + s.remove_field_by_id(&f3); + assert_eq!(s.nth_field_name(1), Some(gstr!("f4"))); + assert!(s.nth_field_name(6).is_none()); + #[cfg(feature = "v1_26")] + assert_eq!(s.nth_field_by_id(2), Some(&f5)); + #[cfg(feature = "v1_26")] + assert!(s.nth_field_by_id(6).is_none()); + assert_eq!( + s.fields().collect::>(), + vec!["f1", "f4", "f5", "f6", "f7", "f8"] + ); + + s.remove_fields(["f4", "f6"]); + assert_eq!(s.fields().collect::>(), vec!["f1", "f5", "f7", "f8"]); + + s.remove_field_by_ids([&f5, &f8]); + assert_eq!(s.fields().collect::>(), vec!["f1", "f7"]); + #[cfg(feature = "v1_26")] + assert_eq!(s.field_ids().collect::>(), vec!["f1", "f7"]); + + s.remove_all_fields(); + assert!(s.is_empty()); + } + + #[cfg(feature = "v1_26")] + #[test] + fn map_in_place() { + crate::init().unwrap(); + + let f1 = idstr!("f1"); + let f2 = idstr!("f2"); + let f3 = idstr!("f3"); + let mut s = Structure::builder_from_id(idstr!("test")) + .field_with_id(&f1, "abc") + .field_with_id(&f2, "bcd") + .field_with_id(&f3, false) + .build(); + assert!(!s.get_by_id::(&f3).unwrap()); + + s.map_in_place_by_id(|name, value| { + if *name == f3 { + *value = true.into() + } + + std::ops::ControlFlow::Continue(()) + }); + assert!(s.get_by_id::(&f3).unwrap()); + + s.map_in_place_by_id(|name, value| { + match name.as_str() { + "f2" => return std::ops::ControlFlow::Break(()), + "f3" => *value = false.into(), + _ => (), + } + std::ops::ControlFlow::Continue(()) + }); + assert!(s.get_by_id::(&f3).unwrap()); + + s.filter_map_in_place_by_id(|name, value| { + if *name == f3 && value.get::().unwrap() { + None + } else { + Some(value) + } + }); + + assert_eq!(s.field_ids().collect::>(), vec![&f1, &f2]); } } diff --git a/gstreamer/src/structure_serde.rs b/gstreamer/src/structure_serde.rs index 2dba774a7..f54c1c4f8 100644 --- a/gstreamer/src/structure_serde.rs +++ b/gstreamer/src/structure_serde.rs @@ -16,7 +16,7 @@ use crate::{ date_time_serde, value::*, value_serde::*, Buffer, DateTime, Sample, Structure, StructureRef, }; -struct FieldSe<'a>(&'static str, &'a glib::SendValue); +struct FieldSe<'a>(&'a str, &'a glib::SendValue); impl<'a> Serialize for FieldSe<'a> { fn serialize(&self, serializer: S) -> Result { ser_value!(self.1, |type_, value| { diff --git a/gstreamer/src/value.rs b/gstreamer/src/value.rs index 94ea17f13..c1310051c 100644 --- a/gstreamer/src/value.rs +++ b/gstreamer/src/value.rs @@ -1481,6 +1481,34 @@ macro_rules! impl_builder_gvalue_extra_setters ( } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[inline] + pub fn field_with_static_if(self, name: impl AsRef<$crate::glib::GStr> + 'static, value: impl Into<$crate::glib::Value> + Send, predicate: bool) -> Self { + if predicate { + self.field_with_static(name, value) + } else { + self + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if the `predicate` evaluates to `true`. + /// + /// This has no effect if the `predicate` evaluates to `false`, + /// i.e. default or previous value for `name` is kept. + #[inline] + pub fn field_with_id_if(self, name: impl AsRef<$crate::IdStr>, value: impl Into<$crate::glib::Value> + Send, predicate: bool) -> Self { + if predicate { + self.field_with_id(name, value) + } else { + self + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` to the given inner value if `value` is `Some`. /// @@ -1494,6 +1522,32 @@ macro_rules! impl_builder_gvalue_extra_setters ( } } + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[inline] + pub fn field_with_static_if_some(self, name: impl AsRef<$crate::glib::GStr> + 'static, value: Option + Send>) -> Self { + if let Some(value) = value { + self.field_with_static(name, value) + } else { + self + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` to the given inner value if `value` is `Some`. + /// + /// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept. + #[inline] + pub fn field_with_id_if_some(self, name: impl AsRef<$crate::IdStr>, value: Option + Send>) -> Self { + if let Some(value) = value { + self.field_with_id(name, value) + } else { + self + } + } + // rustdoc-stripper-ignore-next /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. /// @@ -1508,6 +1562,34 @@ macro_rules! impl_builder_gvalue_extra_setters ( self.field(name, V::from_iter(iter)) } + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_static_from_iter + FromIterator<$crate::glib::SendValue> + Send>( + self, + name: impl AsRef<$crate::glib::GStr> + 'static, + iter: impl IntoIterator, + ) -> Self { + let iter = iter.into_iter().map(|item| item.to_send_value()); + self.field_with_static(name, V::from_iter(iter)) + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s the `Item`s. + /// + /// Overrides any default or previously defined value for `name`. + #[inline] + pub fn field_with_id_from_iter + FromIterator<$crate::glib::SendValue> + Send>( + self, + name: impl AsRef<$crate::IdStr>, + iter: impl IntoIterator, + ) -> Self { + let iter = iter.into_iter().map(|item| item.to_send_value()); + self.field_with_id(name, V::from_iter(iter)) + } + // rustdoc-stripper-ignore-next /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, /// if `iter` is not empty. @@ -1527,6 +1609,46 @@ macro_rules! impl_builder_gvalue_extra_setters ( self } } + + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, + /// if `iter` is not empty. + /// + /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged. + #[inline] + pub fn field_with_static_if_not_empty + FromIterator<$crate::glib::SendValue> + Send>( + self, + name: impl AsRef<$crate::glib::GStr> + 'static, + iter: impl IntoIterator, + ) -> Self { + let mut iter = iter.into_iter().peekable(); + if iter.peek().is_some() { + let iter = iter.map(|item| item.to_send_value()); + self.field_with_static(name, V::from_iter(iter)) + } else { + self + } + } + + // rustdoc-stripper-ignore-next + /// Sets field `name` using the given `ValueType` `V` built from `iter`'s Item`s, + /// if `iter` is not empty. + /// + /// This has no effect if `iter` is empty, i.e. previous value for `name` is unchanged. + #[inline] + pub fn field_with_id_if_not_empty + FromIterator<$crate::glib::SendValue> + Send>( + self, + name: impl AsRef, + iter: impl IntoIterator, + ) -> Self { + let mut iter = iter.into_iter().peekable(); + if iter.peek().is_some() { + let iter = iter.map(|item| item.to_send_value()); + self.field_with_id(name, V::from_iter(iter)) + } else { + self + } + } }; (other_field) => {