pipewire/properties/box_.rs
1// Copyright The pipewire-rs Contributors.
2// SPDX-License-Identifier: MIT
3
4use std::{fmt, mem::ManuallyDrop, ops::Deref, ptr};
5
6use super::Properties;
7
8/// Smart pointer providing unique ownership of a PipeWire [properties](super) object.
9///
10/// For an explanation of these, see [Smart pointers to PipeWire objects](crate#smart-pointers-to-pipewire-objects).
11///
12/// # Examples
13/// Create a `PropertiesBox` struct and access the stored values by key:
14/// ```rust
15/// use pipewire::{properties::{properties, PropertiesBox}};
16///
17/// let props = properties!{
18/// "Key" => "Value",
19/// "OtherKey" => "OtherValue"
20/// };
21///
22/// assert_eq!(Some("Value"), props.get("Key"));
23/// assert_eq!(Some("OtherValue"), props.get("OtherKey"));
24/// ```
25pub struct PropertiesBox {
26 ptr: ptr::NonNull<pw_sys::pw_properties>,
27}
28
29impl PropertiesBox {
30 /// Create a new, initially empty `Properties` struct.
31 pub fn new() -> Self {
32 unsafe {
33 let raw = std::ptr::NonNull::new(pw_sys::pw_properties_new(std::ptr::null()))
34 .expect("Newly created pw_properties should not be null");
35
36 Self::from_raw(raw)
37 }
38 }
39
40 /// Take ownership of an existing raw `pw_properties` pointer.
41 ///
42 /// # Safety
43 /// - The provided pointer must point to a valid, well-aligned `pw_properties` struct.
44 /// - After this call, the returned `PropertiesBox` struct will assume ownership of the data pointed to,
45 /// so that data must not be freed elsewhere.
46 pub unsafe fn from_raw(ptr: ptr::NonNull<pw_sys::pw_properties>) -> Self {
47 Self { ptr }
48 }
49
50 /// Give up ownership of the contained properties , returning a pointer to the raw `pw_properties` struct.
51 ///
52 /// After this function, the caller is responsible for `pw_properties` struct,
53 /// and should make sure it is freed when it is no longer needed.
54 pub fn into_raw(self) -> *mut pw_sys::pw_properties {
55 let this = ManuallyDrop::new(self);
56
57 this.ptr.as_ptr()
58 }
59
60 // TODO: `fn from_string` that calls `pw_sys::pw_properties_new_string`
61 // TODO: bindings for pw_properties_update_keys, pw_properties_update, pw_properties_add, pw_properties_add_keys
62
63 /// Create a new `Properties` from a given dictionary.
64 ///
65 /// All the keys and values from `dict` are copied.
66 pub fn from_dict(dict: &spa::utils::dict::DictRef) -> Self {
67 let ptr = dict.as_raw();
68 unsafe {
69 let copy = pw_sys::pw_properties_new_dict(ptr);
70 Self::from_raw(ptr::NonNull::new(copy).expect("pw_properties_new_dict() returned NULL"))
71 }
72 }
73}
74
75impl AsRef<Properties> for PropertiesBox {
76 fn as_ref(&self) -> &Properties {
77 self.deref()
78 }
79}
80
81impl AsRef<spa::utils::dict::DictRef> for PropertiesBox {
82 fn as_ref(&self) -> &spa::utils::dict::DictRef {
83 self.deref().as_ref()
84 }
85}
86
87impl std::ops::Deref for PropertiesBox {
88 type Target = Properties;
89
90 fn deref(&self) -> &Self::Target {
91 unsafe { self.ptr.cast().as_ref() }
92 }
93}
94
95impl std::ops::DerefMut for PropertiesBox {
96 fn deref_mut(&mut self) -> &mut Self::Target {
97 unsafe { self.ptr.cast().as_mut() }
98 }
99}
100
101impl Default for PropertiesBox {
102 fn default() -> Self {
103 Self::new()
104 }
105}
106
107impl Clone for PropertiesBox {
108 fn clone(&self) -> Self {
109 unsafe {
110 let ptr = pw_sys::pw_properties_copy(self.as_raw_ptr());
111 let ptr = ptr::NonNull::new_unchecked(ptr);
112
113 Self { ptr }
114 }
115 }
116}
117
118impl Drop for PropertiesBox {
119 fn drop(&mut self) {
120 unsafe { pw_sys::pw_properties_free(self.ptr.as_ptr()) }
121 }
122}
123
124impl fmt::Debug for PropertiesBox {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 let dict: &spa::utils::dict::DictRef = self.as_ref();
127 // FIXME: Debug-print dict keys and values directly
128 f.debug_tuple("PropertiesBox").field(dict).finish()
129 }
130}
131
132/// A macro for creating a new `Properties` struct with predefined key-value pairs.
133///
134/// The macro accepts a list of `Key => Value` pairs, separated by commas.
135///
136/// # Examples:
137/// Create a `Properties` struct from literals.
138/// ```rust
139/// use pipewire::properties::properties;
140///
141/// let props = properties!{
142/// "Key1" => "Value1",
143/// "Key2" => "Value2",
144/// };
145/// ```
146///
147/// Any expression that evaluates to a `impl Into<Vec<u8>>` can be used for both keys and values.
148/// ```rust
149/// use pipewire::properties::properties;
150///
151/// let key = String::from("Key");
152/// let value = vec![86, 97, 108, 117, 101]; // "Value" as an ASCII u8 vector.
153/// let props = properties!{
154/// key => value
155/// };
156///
157/// assert_eq!(Some("Value"), props.get("Key"));
158/// ```
159#[macro_export]
160macro_rules! __properties__ {
161 {$($k:expr => $v:expr),+ $(,)?} => {{
162 let mut properties = $crate::properties::PropertiesBox::new();
163 $(
164 properties.insert($k, $v);
165 )*
166 properties
167 }};
168}
169
170pub use __properties__ as properties;