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;