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<K, V> FromIterator<(K, V)> for PropertiesBox
119where
120    K: Into<Vec<u8>>,
121    V: Into<Vec<u8>>,
122{
123    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
124        let mut props = Self::new();
125
126        props.extend(iter);
127
128        props
129    }
130}
131
132impl Drop for PropertiesBox {
133    fn drop(&mut self) {
134        unsafe { pw_sys::pw_properties_free(self.ptr.as_ptr()) }
135    }
136}
137
138impl fmt::Debug for PropertiesBox {
139    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140        let dict: &spa::utils::dict::DictRef = self.as_ref();
141        // FIXME: Debug-print dict keys and values directly
142        f.debug_tuple("PropertiesBox").field(dict).finish()
143    }
144}
145
146/// A macro for creating a new `Properties` struct with predefined key-value pairs.
147///
148/// The macro accepts a list of `Key => Value` pairs, separated by commas.
149///
150/// # Examples:
151/// Create a `Properties` struct from literals.
152/// ```rust
153/// use pipewire::properties::properties;
154///
155/// let props = properties!{
156///    "Key1" => "Value1",
157///    "Key2" => "Value2",
158/// };
159/// ```
160///
161/// Any expression that evaluates to a `impl Into<Vec<u8>>` can be used for both keys and values.
162/// ```rust
163/// use pipewire::properties::properties;
164///
165/// let key = String::from("Key");
166/// let value = vec![86, 97, 108, 117, 101]; // "Value" as an ASCII u8 vector.
167/// let props = properties!{
168///     key => value
169/// };
170///
171/// assert_eq!(Some("Value"), props.get("Key"));
172/// ```
173#[macro_export]
174macro_rules! __properties__ {
175    {$($k:expr => $v:expr),+ $(,)?} => {{
176        let mut properties = $crate::properties::PropertiesBox::new();
177        $(
178            properties.insert($k, $v);
179        )*
180        properties
181    }};
182}
183
184pub use __properties__ as properties;