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