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;