mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-11-21 17:11:04 +00:00
all: GObject builders: generalise property_from_str
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1594>
This commit is contained in:
parent
68571f6f75
commit
9c386085e3
8 changed files with 551 additions and 237 deletions
|
@ -512,9 +512,13 @@ impl AppSink {
|
|||
/// Creates a new builder-pattern struct instance to construct [`AppSink`] objects.
|
||||
///
|
||||
/// This method returns an instance of [`AppSinkBuilder`](crate::builders::AppSinkBuilder) which can be used to create [`AppSink`] objects.
|
||||
pub fn builder() -> AppSinkBuilder {
|
||||
pub fn builder<'a>() -> AppSinkBuilder<'a> {
|
||||
assert_initialized_main_thread!();
|
||||
AppSinkBuilder::new()
|
||||
AppSinkBuilder {
|
||||
builder: gst::Object::builder(),
|
||||
callbacks: None,
|
||||
drop_out_of_segment: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "gst_app_sink_set_callbacks")]
|
||||
|
@ -1173,26 +1177,23 @@ impl AppSink {
|
|||
///
|
||||
/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct AppSinkBuilder {
|
||||
builder: glib::object::ObjectBuilder<'static, AppSink>,
|
||||
pub struct AppSinkBuilder<'a> {
|
||||
builder: gst::gobject::GObjectBuilder<'a, AppSink>,
|
||||
callbacks: Option<AppSinkCallbacks>,
|
||||
drop_out_of_segment: Option<bool>,
|
||||
}
|
||||
|
||||
impl AppSinkBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
builder: glib::Object::builder(),
|
||||
callbacks: None,
|
||||
drop_out_of_segment: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AppSinkBuilder<'a> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Build the [`AppSink`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if the [`AppSink`] doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
#[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
|
||||
pub fn build(self) -> AppSink {
|
||||
let appsink = self.builder.build();
|
||||
let appsink = self.builder.build().unwrap();
|
||||
|
||||
if let Some(callbacks) = self.callbacks {
|
||||
appsink.set_callbacks(callbacks);
|
||||
|
@ -1226,7 +1227,7 @@ impl AppSinkBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn caps(self, caps: &gst::Caps) -> Self {
|
||||
pub fn caps(self, caps: &'a gst::Caps) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("caps", caps),
|
||||
..self
|
||||
|
@ -1350,12 +1351,29 @@ impl AppSinkBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
/// Overrides any default or previously defined value for `name`.
|
||||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("name", name.into()),
|
||||
builder: self.builder.property(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property_from_str(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
gst::impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -311,9 +311,13 @@ impl AppSrc {
|
|||
/// Creates a new builder-pattern struct instance to construct [`AppSrc`] objects.
|
||||
///
|
||||
/// This method returns an instance of [`AppSrcBuilder`](crate::builders::AppSrcBuilder) which can be used to create [`AppSrc`] objects.
|
||||
pub fn builder() -> AppSrcBuilder {
|
||||
pub fn builder<'a>() -> AppSrcBuilder<'a> {
|
||||
assert_initialized_main_thread!();
|
||||
AppSrcBuilder::new()
|
||||
AppSrcBuilder {
|
||||
builder: gst::Object::builder(),
|
||||
callbacks: None,
|
||||
automatic_eos: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "gst_app_src_set_callbacks")]
|
||||
|
@ -453,26 +457,23 @@ impl AppSrc {
|
|||
///
|
||||
/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct AppSrcBuilder {
|
||||
builder: glib::object::ObjectBuilder<'static, AppSrc>,
|
||||
pub struct AppSrcBuilder<'a> {
|
||||
builder: gst::gobject::GObjectBuilder<'a, AppSrc>,
|
||||
callbacks: Option<AppSrcCallbacks>,
|
||||
automatic_eos: Option<bool>,
|
||||
}
|
||||
|
||||
impl AppSrcBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
builder: glib::Object::builder(),
|
||||
callbacks: None,
|
||||
automatic_eos: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AppSrcBuilder<'a> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Build the [`AppSrc`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if the [`AppSrc`] doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
#[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
|
||||
pub fn build(self) -> AppSrc {
|
||||
let appsrc = self.builder.build();
|
||||
let appsrc = self.builder.build().unwrap();
|
||||
|
||||
if let Some(callbacks) = self.callbacks {
|
||||
appsrc.set_callbacks(callbacks);
|
||||
|
@ -506,7 +507,7 @@ impl AppSrcBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn caps(self, caps: &gst::Caps) -> Self {
|
||||
pub fn caps(self, caps: &'a gst::Caps) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("caps", caps),
|
||||
..self
|
||||
|
@ -621,12 +622,29 @@ impl AppSrcBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
/// Overrides any default or previously defined value for `name`.
|
||||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("name", name.into()),
|
||||
builder: self.builder.property(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property_from_str(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
gst::impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -792,4 +810,37 @@ mod tests {
|
|||
sample_quantity
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_caps_lt() {
|
||||
gst::init().unwrap();
|
||||
|
||||
let caps = &gst::Caps::new_any();
|
||||
{
|
||||
let stream_type = "random-access".to_owned();
|
||||
let appsrc = AppSrc::builder()
|
||||
.property_from_str("stream-type", &stream_type)
|
||||
.caps(caps)
|
||||
.build();
|
||||
assert_eq!(
|
||||
appsrc.property::<crate::AppStreamType>("stream-type"),
|
||||
crate::AppStreamType::RandomAccess
|
||||
);
|
||||
assert!(appsrc.property::<gst::Caps>("caps").is_any());
|
||||
}
|
||||
|
||||
let stream_type = &"random-access".to_owned();
|
||||
{
|
||||
let caps = &gst::Caps::new_any();
|
||||
let appsrc = AppSrc::builder()
|
||||
.property_from_str("stream-type", stream_type)
|
||||
.caps(caps)
|
||||
.build();
|
||||
assert_eq!(
|
||||
appsrc.property::<crate::AppStreamType>("stream-type"),
|
||||
crate::AppStreamType::RandomAccess
|
||||
);
|
||||
assert!(appsrc.property::<gst::Caps>("caps").is_any());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,11 @@ impl Bin {
|
|||
/// Creates a new builder-pattern struct instance to construct [`Bin`] objects.
|
||||
///
|
||||
/// This method returns an instance of [`BinBuilder`] which can be used to create [`Bin`] objects.
|
||||
pub fn builder() -> BinBuilder {
|
||||
BinBuilder::new()
|
||||
pub fn builder<'a>() -> BinBuilder<'a> {
|
||||
assert_initialized_main_thread!();
|
||||
BinBuilder {
|
||||
builder: crate::Object::builder(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,22 +224,21 @@ impl Default for Bin {
|
|||
///
|
||||
/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct BinBuilder {
|
||||
builder: glib::object::ObjectBuilder<'static, Bin>,
|
||||
pub struct BinBuilder<'a> {
|
||||
builder: crate::gobject::GObjectBuilder<'a, Bin>,
|
||||
}
|
||||
|
||||
impl BinBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
builder: glib::Object::builder(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BinBuilder<'a> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Build the [`Bin`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if the [`Bin`] doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
#[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
|
||||
pub fn build(self) -> Bin {
|
||||
self.builder.build()
|
||||
self.builder.build().unwrap()
|
||||
}
|
||||
|
||||
pub fn async_handling(self, async_handling: bool) -> Self {
|
||||
|
@ -267,27 +269,27 @@ impl BinBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
/// Overrides any default or previously defined value for `name`.
|
||||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("name", name.into()),
|
||||
builder: self.builder.property(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_if(self, name: impl Into<glib::GString>, predicate: bool) -> Self {
|
||||
if predicate {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property_from_str(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_if_some(self, name: Option<impl Into<glib::GString>>) -> Self {
|
||||
if let Some(name) = name {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn do_latency_trampoline<
|
||||
|
@ -346,4 +348,20 @@ mod tests {
|
|||
vec![String::from("identity0"), String::from("identity1")]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let msg_fwd = "message-forward";
|
||||
let bin = Bin::builder()
|
||||
.name("test-bin")
|
||||
.property("async-handling", true)
|
||||
.property_from_str(msg_fwd, "True")
|
||||
.build();
|
||||
|
||||
assert_eq!(bin.name(), "test-bin");
|
||||
assert!(bin.property::<bool>("async-handling"));
|
||||
assert!(bin.property::<bool>("message-forward"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,9 @@ impl ElementFactory {
|
|||
#[track_caller]
|
||||
pub fn create(&self) -> ElementBuilder {
|
||||
assert_initialized_main_thread!();
|
||||
|
||||
ElementBuilder {
|
||||
name_or_factory: NameOrFactory::Factory(self),
|
||||
properties: smallvec::SmallVec::new(),
|
||||
builder: crate::Object::builder_for_deferred_type(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,10 +27,9 @@ impl ElementFactory {
|
|||
#[track_caller]
|
||||
pub fn make(factoryname: &str) -> ElementBuilder {
|
||||
assert_initialized_main_thread!();
|
||||
|
||||
ElementBuilder {
|
||||
name_or_factory: NameOrFactory::Name(factoryname),
|
||||
properties: smallvec::SmallVec::new(),
|
||||
builder: crate::Object::builder_for_deferred_type(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,7 +188,7 @@ impl ElementFactory {
|
|||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct ElementBuilder<'a> {
|
||||
name_or_factory: NameOrFactory<'a>,
|
||||
properties: smallvec::SmallVec<[(&'a str, ValueOrStr<'a>); 16]>,
|
||||
builder: crate::gobject::GObjectBuilder<'a, Element>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -199,30 +197,7 @@ enum NameOrFactory<'a> {
|
|||
Factory(&'a ElementFactory),
|
||||
}
|
||||
|
||||
enum ValueOrStr<'a> {
|
||||
Value(glib::Value),
|
||||
Str(&'a str),
|
||||
}
|
||||
|
||||
impl<'a> ElementBuilder<'a> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the name property to the given `name`.
|
||||
#[inline]
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
self.property("name", name.into())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the name property to the given `name` if it is `Some`.
|
||||
#[inline]
|
||||
pub fn name_if_some(self, name: Option<impl Into<glib::GString>>) -> Self {
|
||||
if let Some(name) = name {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
|
@ -230,51 +205,34 @@ impl<'a> ElementBuilder<'a> {
|
|||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
name_or_factory: self.name_or_factory,
|
||||
properties: {
|
||||
let mut properties = self.properties;
|
||||
properties.push((name, ValueOrStr::Value(value.into())));
|
||||
properties
|
||||
},
|
||||
builder: self.builder.property(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
impl_builder_gvalue_extra_setters!(property);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
name_or_factory: self.name_or_factory,
|
||||
properties: {
|
||||
let mut properties = self.properties;
|
||||
properties.push((name, ValueOrStr::Str(value)));
|
||||
properties
|
||||
},
|
||||
builder: self.builder.property_from_str(name, value),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value` if it is `Some`.
|
||||
#[inline]
|
||||
pub fn property_from_str_if_some(self, name: &'a str, value: Option<&'a str>) -> Self {
|
||||
if let Some(value) = value {
|
||||
self.property_from_str(name, value)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builds the element with the provided properties.
|
||||
/// Builds the [`Element`] with the provided properties.
|
||||
///
|
||||
/// This fails if there is no such element factory or the element factory can't be loaded.
|
||||
/// This fails if there is no such [`ElementFactory`] or the [`ElementFactory`] can't be loaded.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if the element is not instantiable, doesn't have all the given properties or
|
||||
/// This panics if the [`Element`] is not instantiable, doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
///
|
||||
/// [`Element`]: crate::Element
|
||||
#[track_caller]
|
||||
#[must_use = "Building the element without using it has no effect"]
|
||||
pub fn build(self) -> Result<Element, glib::BoolError> {
|
||||
|
@ -328,68 +286,21 @@ impl<'a> ElementBuilder<'a> {
|
|||
));
|
||||
}
|
||||
|
||||
let mut properties = smallvec::SmallVec::<[_; 16]>::with_capacity(self.properties.len());
|
||||
let klass = glib::Class::<Element>::from_type(element_type).unwrap();
|
||||
for (name, value) in self.properties {
|
||||
match value {
|
||||
ValueOrStr::Value(value) => {
|
||||
properties.push((name, value));
|
||||
let element = self
|
||||
.builder
|
||||
.type_(element_type)
|
||||
.build()
|
||||
.map_err(|err| {
|
||||
use crate::gobject::GObjectError::*;
|
||||
match err {
|
||||
PropertyNotFound { property, .. } => {
|
||||
format!("property '{property}' of element factory '{}' not found", factory.name())
|
||||
},
|
||||
PropertyFromStr { property, value, .. } => {
|
||||
format!("property '{property}' of element factory '{}' can't be set from string '{value}'", factory.name())
|
||||
},
|
||||
}
|
||||
ValueOrStr::Str(value) => {
|
||||
use crate::value::GstValueExt;
|
||||
|
||||
let pspec = match klass.find_property(name) {
|
||||
Some(pspec) => pspec,
|
||||
None => {
|
||||
panic!(
|
||||
"property '{}' of element factory '{}' not found",
|
||||
name,
|
||||
factory.name()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let value = {
|
||||
if pspec.value_type() == crate::Structure::static_type() && value == "NULL"
|
||||
{
|
||||
None::<crate::Structure>.to_value()
|
||||
} else {
|
||||
#[cfg(feature = "v1_20")]
|
||||
{
|
||||
glib::Value::deserialize_with_pspec(value, &pspec)
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"property '{}' of element factory '{}' can't be set from string '{}'",
|
||||
name,
|
||||
factory.name(),
|
||||
value,
|
||||
)
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "v1_20"))]
|
||||
{
|
||||
glib::Value::deserialize(value, pspec.value_type())
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"property '{}' of element factory '{}' can't be set from string '{}'",
|
||||
name,
|
||||
factory.name(),
|
||||
value,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
properties.push((name, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let element = unsafe {
|
||||
glib::Object::with_mut_values(element_type, &mut properties)
|
||||
.unsafe_cast::<crate::Element>()
|
||||
};
|
||||
}).unwrap();
|
||||
|
||||
unsafe {
|
||||
use std::sync::atomic;
|
||||
|
@ -430,3 +341,27 @@ impl<'a> ElementBuilder<'a> {
|
|||
Ok(element)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn builder() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let fakesink = ElementFactory::make("fakesink")
|
||||
.name("test-fakesink")
|
||||
.property("can-activate-pull", true)
|
||||
.property_from_str("state-error", "ready-to-paused")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fakesink.name(), "test-fakesink");
|
||||
assert!(fakesink.property::<bool>("can-activate-pull"));
|
||||
let v = fakesink.property_value("state-error");
|
||||
let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();
|
||||
assert_eq!(e.nick(), "ready-to-paused");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,98 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use glib::prelude::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::value::GstValueExt;
|
||||
use glib::{object::IsClass, prelude::*, Type};
|
||||
|
||||
use crate::{value::GstValueExt, IdStr};
|
||||
|
||||
impl crate::Object {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builds a `GObjectBuilder` targeting type `O`.
|
||||
#[inline]
|
||||
pub fn builder<'a, O>() -> GObjectBuilder<'a, O>
|
||||
where
|
||||
O: IsA<crate::Object> + IsClass,
|
||||
{
|
||||
assert_initialized_main_thread!();
|
||||
GObjectBuilder {
|
||||
type_: Some(O::static_type()),
|
||||
properties: smallvec::SmallVec::new(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builds a `GObjectBuilder` targeting base class of type `O` and concrete `type_`.
|
||||
#[inline]
|
||||
pub fn builder_for<'a, O>(type_: Type) -> GObjectBuilder<'a, O>
|
||||
where
|
||||
O: IsA<crate::Object> + IsClass,
|
||||
{
|
||||
assert_initialized_main_thread!();
|
||||
GObjectBuilder {
|
||||
type_: Some(type_),
|
||||
properties: smallvec::SmallVec::new(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builds a `GObjectBuilder` targeting base class of type `O`
|
||||
/// and a concrete `Type` that will be specified later.
|
||||
///
|
||||
/// This is useful when the concrete type of the object is dynamically determined
|
||||
/// when calling the `build()` method of a wrapping builder.
|
||||
#[inline]
|
||||
pub fn builder_for_deferred_type<'a, O>() -> GObjectBuilder<'a, O>
|
||||
where
|
||||
O: IsA<crate::Object> + IsClass,
|
||||
{
|
||||
assert_initialized_main_thread!();
|
||||
GObjectBuilder {
|
||||
type_: None,
|
||||
properties: smallvec::SmallVec::new(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
|
||||
pub enum GObjectError {
|
||||
#[error("property {property} for type {type_} not found")]
|
||||
PropertyNotFound { type_: Type, property: IdStr },
|
||||
|
||||
#[error("property {property} for type {type_} can't be set from string {value}")]
|
||||
PropertyFromStr {
|
||||
type_: Type,
|
||||
property: IdStr,
|
||||
value: IdStr,
|
||||
},
|
||||
}
|
||||
|
||||
fn value_from_property_str(
|
||||
pspec: glib::ParamSpec,
|
||||
value: &str,
|
||||
) -> Result<glib::Value, GObjectError> {
|
||||
skip_assert_initialized!(); // Already checked transitively by caller
|
||||
|
||||
if pspec.value_type() == crate::Structure::static_type() && value == "NULL" {
|
||||
Ok(None::<crate::Structure>.to_value())
|
||||
} else {
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "v1_20")] {
|
||||
let res = glib::Value::deserialize_with_pspec(value, &pspec);
|
||||
} else {
|
||||
let res = glib::Value::deserialize(value, pspec.value_type());
|
||||
}
|
||||
}
|
||||
res.map_err(|_| GObjectError::PropertyFromStr {
|
||||
type_: pspec.owner_type(),
|
||||
property: pspec.name().into(),
|
||||
value: value.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GObjectExtManualGst: IsA<glib::Object> + 'static {
|
||||
#[doc(alias = "gst_util_set_object_arg")]
|
||||
|
@ -12,53 +102,192 @@ pub trait GObjectExtManualGst: IsA<glib::Object> + 'static {
|
|||
panic!("property '{}' of type '{}' not found", name, self.type_());
|
||||
});
|
||||
|
||||
let value = {
|
||||
if pspec.value_type() == crate::Structure::static_type() && value == "NULL" {
|
||||
None::<crate::Structure>.to_value()
|
||||
} else {
|
||||
#[cfg(feature = "v1_20")]
|
||||
{
|
||||
glib::Value::deserialize_with_pspec(value, &pspec).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"property '{}' of type '{}' can't be set from string '{}'",
|
||||
name,
|
||||
self.type_(),
|
||||
value,
|
||||
)
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "v1_20"))]
|
||||
{
|
||||
glib::Value::deserialize(value, pspec.value_type()).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"property '{}' of type '{}' can't be set from string '{}'",
|
||||
name,
|
||||
self.type_(),
|
||||
value,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.set_property(name, value)
|
||||
self.set_property(name, value_from_property_str(pspec, value).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: IsA<glib::Object>> GObjectExtManualGst for O {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builder for `GObject`s.
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct GObjectBuilder<'a, O> {
|
||||
type_: Option<Type>,
|
||||
properties: smallvec::SmallVec<[(&'a str, ValueOrStr<'a>); 16]>,
|
||||
phantom: PhantomData<O>,
|
||||
}
|
||||
|
||||
enum ValueOrStr<'a> {
|
||||
Value(glib::Value),
|
||||
Str(&'a str),
|
||||
}
|
||||
|
||||
impl<'a, O: IsA<crate::Object> + IsClass> GObjectBuilder<'a, O> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the concrete `Type`.
|
||||
///
|
||||
/// This should be used on an `GObjectBuilder` created with
|
||||
/// [`GObjectBuilder::for_deferred_type`].
|
||||
#[inline]
|
||||
pub fn type_(mut self, type_: Type) -> Self {
|
||||
self.type_ = Some(type_);
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
/// Overrides any default or previously defined value for `name`.
|
||||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
properties: {
|
||||
let mut properties = self.properties;
|
||||
properties.push((name, ValueOrStr::Value(value.into())));
|
||||
properties
|
||||
},
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
properties: {
|
||||
let mut properties = self.properties;
|
||||
properties.push((name, ValueOrStr::Str(value)));
|
||||
properties
|
||||
},
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builds the [`Object`] with the provided properties.
|
||||
///
|
||||
/// This fails if there is no such element factory or the element factory can't be loaded.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if:
|
||||
///
|
||||
/// * The [`Object`] is not instantiable, doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
/// * The [`GObjectBuilder`] was created for a deferred concrete `Type` but
|
||||
/// the `Type` was not set.
|
||||
///
|
||||
/// [`Object`]: crate::Object
|
||||
#[track_caller]
|
||||
#[must_use = "Building the element without using it has no effect"]
|
||||
pub fn build(self) -> Result<O, GObjectError> {
|
||||
let type_ = self.type_.expect("Deferred Type must be set");
|
||||
|
||||
let mut properties = smallvec::SmallVec::<[_; 16]>::with_capacity(self.properties.len());
|
||||
let klass = glib::Class::<O>::from_type(type_).unwrap();
|
||||
for (name, value) in self.properties {
|
||||
let pspec =
|
||||
klass
|
||||
.find_property(name)
|
||||
.ok_or_else(|| GObjectError::PropertyNotFound {
|
||||
type_,
|
||||
property: name.into(),
|
||||
})?;
|
||||
|
||||
match value {
|
||||
ValueOrStr::Value(value) => properties.push((name, value)),
|
||||
ValueOrStr::Str(value) => {
|
||||
properties.push((name, value_from_property_str(pspec, value)?));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let object =
|
||||
unsafe { glib::Object::with_mut_values(type_, &mut properties).unsafe_cast::<O>() };
|
||||
|
||||
Ok(object)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{prelude::*, Bin, Element, ElementFactory, Object};
|
||||
|
||||
#[test]
|
||||
fn test_set_property_from_str() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let fakesink = crate::ElementFactory::make("fakesink").build().unwrap();
|
||||
let fakesink = ElementFactory::make("fakesink").build().unwrap();
|
||||
fakesink.set_property_from_str("state-error", "ready-to-paused");
|
||||
let v = fakesink.property_value("state-error");
|
||||
let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();
|
||||
assert_eq!(e.nick(), "ready-to-paused");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let msg_fwd = "message-forward";
|
||||
let bin = Object::builder::<Bin>()
|
||||
.name("test-bin")
|
||||
.property("async-handling", true)
|
||||
.property_from_str(msg_fwd, "True")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(bin.name(), "test-bin");
|
||||
assert!(bin.property::<bool>("async-handling"));
|
||||
assert!(bin.property::<bool>("message-forward"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_err() {
|
||||
crate::init().unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Object::builder::<Bin>()
|
||||
.property("not-a-prop", true)
|
||||
.build(),
|
||||
Err(GObjectError::PropertyNotFound {
|
||||
type_: Bin::static_type(),
|
||||
property: idstr!("not-a-prop")
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Object::builder::<Bin>()
|
||||
.property_from_str("async-handling", "not-a-bool")
|
||||
.build(),
|
||||
Err(GObjectError::PropertyFromStr {
|
||||
type_: Bin::static_type(),
|
||||
property: idstr!("async-handling"),
|
||||
value: idstr!("not-a-bool")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builder_for() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let fakesink = ElementFactory::make("fakesink").build().unwrap();
|
||||
|
||||
let fakesink = Object::builder_for::<Element>(fakesink.type_())
|
||||
.name("test-fakesink")
|
||||
.property("can-activate-pull", true)
|
||||
.property_from_str("state-error", "ready-to-paused")
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fakesink.name(), "test-fakesink");
|
||||
assert!(fakesink.property::<bool>("can-activate-pull"));
|
||||
let v = fakesink.property_value("state-error");
|
||||
let (_klass, e) = glib::EnumValue::from_value(&v).unwrap();
|
||||
assert_eq!(e.nick(), "ready-to-paused");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ mod device_provider;
|
|||
mod device_provider_factory;
|
||||
mod enums;
|
||||
mod ghost_pad;
|
||||
mod gobject;
|
||||
pub mod gobject;
|
||||
mod iterator;
|
||||
mod object;
|
||||
mod pad;
|
||||
|
|
|
@ -35,8 +35,11 @@ impl Pipeline {
|
|||
/// Creates a new builder-pattern struct instance to construct [`Pipeline`] objects.
|
||||
///
|
||||
/// This method returns an instance of [`PipelineBuilder`] which can be used to create [`Pipeline`] objects.
|
||||
pub fn builder() -> PipelineBuilder {
|
||||
PipelineBuilder::new()
|
||||
pub fn builder<'a>() -> PipelineBuilder<'a> {
|
||||
assert_initialized_main_thread!();
|
||||
PipelineBuilder {
|
||||
builder: crate::Object::builder(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,22 +83,21 @@ impl Default for Pipeline {
|
|||
///
|
||||
/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct PipelineBuilder {
|
||||
builder: glib::object::ObjectBuilder<'static, Pipeline>,
|
||||
pub struct PipelineBuilder<'a> {
|
||||
builder: crate::gobject::GObjectBuilder<'a, Pipeline>,
|
||||
}
|
||||
|
||||
impl PipelineBuilder {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
builder: glib::Object::builder(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PipelineBuilder<'a> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Build the [`Pipeline`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This panics if the [`Pipeline`] doesn't have all the given properties or
|
||||
/// property values of the wrong type are provided.
|
||||
#[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"]
|
||||
pub fn build(self) -> Pipeline {
|
||||
self.builder.build()
|
||||
self.builder.build().unwrap()
|
||||
}
|
||||
|
||||
pub fn auto_flush_bus(self, auto_flush_bus: bool) -> Self {
|
||||
|
@ -188,25 +190,25 @@ impl PipelineBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given value `value`.
|
||||
///
|
||||
/// Overrides any default or previously defined value for `name`.
|
||||
#[inline]
|
||||
pub fn property(self, name: &'a str, value: impl Into<glib::Value> + 'a) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("name", name.into()),
|
||||
builder: self.builder.property(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_if(self, name: impl Into<glib::GString>, predicate: bool) -> Self {
|
||||
if predicate {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value`.
|
||||
#[inline]
|
||||
pub fn property_from_str(self, name: &'a str, value: &'a str) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property_from_str(name, value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_if_some(self, name: Option<impl Into<glib::GString>>) -> Self {
|
||||
if let Some(name) = name {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
impl_builder_gvalue_extra_setters!(property_and_name);
|
||||
}
|
||||
|
|
|
@ -1714,7 +1714,7 @@ macro_rules! impl_builder_gvalue_extra_setters (
|
|||
}
|
||||
};
|
||||
|
||||
(property) => {
|
||||
(property_and_name) => {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given inner value if the `predicate` evaluates to `true`.
|
||||
///
|
||||
|
@ -1775,6 +1775,67 @@ macro_rules! impl_builder_gvalue_extra_setters (
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `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 property_from_str_if(self, name: &'a str, value: &'a str, predicate: bool) -> Self {
|
||||
if predicate {
|
||||
self.property_from_str(name, value)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets property `name` to the given string value `value` if it is `Some`.
|
||||
///
|
||||
/// This has no effect if the value is `None`, i.e. default or previous value for `name` is kept.
|
||||
#[inline]
|
||||
pub fn property_from_str_if_some(self, name: &'a str, value: Option<&'a str>) -> Self {
|
||||
if let Some(value) = value {
|
||||
self.property_from_str(name, value)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the name property to the given `name`.
|
||||
#[inline]
|
||||
pub fn name(self, name: impl Into<$crate::glib::GString>) -> Self {
|
||||
self.property("name", name.into())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the name property to the given `name` if the `predicate` evaluates to `true`.
|
||||
///
|
||||
/// This has no effect if the `predicate` evaluates to `false`,
|
||||
/// i.e. default or previous name is kept.
|
||||
#[inline]
|
||||
pub fn name_if(self, name: impl Into<$crate::glib::GString>, predicate: bool) -> Self {
|
||||
if predicate {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the name property to the given `name` if it is `Some`.
|
||||
///
|
||||
/// This has no effect if the value is `None`, i.e. default or previous name is kept.
|
||||
#[inline]
|
||||
pub fn name_if_some(self, name: Option<impl Into<$crate::glib::GString>>) -> Self {
|
||||
if let Some(name) = name {
|
||||
self.name(name)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue