libpulse_binding/
proplist.rs

1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! Property list constants and functions.
15
16use std::os::raw::{c_char, c_void};
17use std::ffi::{CStr, CString};
18use std::ptr::{null, null_mut};
19use std::marker::PhantomData;
20use crate::error::PAErr;
21
22pub(crate) use capi::pa_proplist as ProplistInternal;
23pub use capi::pa_update_mode_t as UpdateMode;
24
25/// Common properties.
26pub mod properties {
27    use capi;
28
29    pub use capi::PA_PROP_MEDIA_NAME as MEDIA_NAME;
30    pub use capi::PA_PROP_MEDIA_TITLE as MEDIA_TITLE;
31    pub use capi::PA_PROP_MEDIA_ARTIST as MEDIA_ARTIST;
32    pub use capi::PA_PROP_MEDIA_COPYRIGHT as MEDIA_COPYRIGHT;
33    pub use capi::PA_PROP_MEDIA_SOFTWARE as MEDIA_SOFTWARE;
34    pub use capi::PA_PROP_MEDIA_LANGUAGE as MEDIA_LANGUAGE;
35    pub use capi::PA_PROP_MEDIA_FILENAME as MEDIA_FILENAME;
36    pub use capi::PA_PROP_MEDIA_ICON as MEDIA_ICON;
37    pub use capi::PA_PROP_MEDIA_ICON_NAME as MEDIA_ICON_NAME;
38    pub use capi::PA_PROP_MEDIA_ROLE as MEDIA_ROLE;
39    pub use capi::PA_PROP_FILTER_WANT as FILTER_WANT;
40    pub use capi::PA_PROP_EVENT_ID as EVENT_ID;
41    pub use capi::PA_PROP_EVENT_DESCRIPTION as EVENT_DESCRIPTION;
42    pub use capi::PA_PROP_EVENT_MOUSE_X as EVENT_MOUSE_X;
43    pub use capi::PA_PROP_EVENT_MOUSE_Y as EVENT_MOUSE_Y;
44    pub use capi::PA_PROP_EVENT_MOUSE_HPOS as EVENT_MOUSE_HPOS;
45    pub use capi::PA_PROP_EVENT_MOUSE_VPOS as EVENT_MOUSE_VPOS;
46    pub use capi::PA_PROP_EVENT_MOUSE_BUTTON as EVENT_MOUSE_BUTTON;
47    pub use capi::PA_PROP_WINDOW_NAME as WINDOW_NAME;
48    pub use capi::PA_PROP_WINDOW_ID as WINDOW_ID;
49    pub use capi::PA_PROP_WINDOW_ICON as WINDOW_ICON;
50    pub use capi::PA_PROP_WINDOW_ICON_NAME as WINDOW_ICON_NAME;
51    pub use capi::PA_PROP_WINDOW_X as WINDOW_X;
52    pub use capi::PA_PROP_WINDOW_Y as WINDOW_Y;
53    pub use capi::PA_PROP_WINDOW_WIDTH as WINDOW_WIDTH;
54    pub use capi::PA_PROP_WINDOW_HEIGHT as WINDOW_HEIGHT;
55    pub use capi::PA_PROP_WINDOW_HPOS as WINDOW_HPOS;
56    pub use capi::PA_PROP_WINDOW_VPOS as WINDOW_VPOS;
57    pub use capi::PA_PROP_WINDOW_DESKTOP as WINDOW_DESKTOP;
58    pub use capi::PA_PROP_WINDOW_X11_DISPLAY as WINDOW_X11_DISPLAY;
59    pub use capi::PA_PROP_WINDOW_X11_SCREEN as WINDOW_X11_SCREEN;
60    pub use capi::PA_PROP_WINDOW_X11_MONITOR as WINDOW_X11_MONITOR;
61    pub use capi::PA_PROP_WINDOW_X11_XID as WINDOW_X11_XID;
62    pub use capi::PA_PROP_APPLICATION_NAME as APPLICATION_NAME;
63    pub use capi::PA_PROP_APPLICATION_ID as APPLICATION_ID;
64    pub use capi::PA_PROP_APPLICATION_VERSION as APPLICATION_VERSION;
65    pub use capi::PA_PROP_APPLICATION_ICON as APPLICATION_ICON;
66    pub use capi::PA_PROP_APPLICATION_ICON_NAME as APPLICATION_ICON_NAME;
67    pub use capi::PA_PROP_APPLICATION_LANGUAGE as APPLICATION_LANGUAGE;
68    pub use capi::PA_PROP_APPLICATION_PROCESS_ID as APPLICATION_PROCESS_ID;
69    pub use capi::PA_PROP_APPLICATION_PROCESS_BINARY as APPLICATION_PROCESS_BINARY;
70    pub use capi::PA_PROP_APPLICATION_PROCESS_USER as APPLICATION_PROCESS_USER;
71    pub use capi::PA_PROP_APPLICATION_PROCESS_HOST as APPLICATION_PROCESS_HOST;
72    pub use capi::PA_PROP_APPLICATION_PROCESS_MACHINE_ID as APPLICATION_PROCESS_MACHINE_ID;
73    pub use capi::PA_PROP_APPLICATION_PROCESS_SESSION_ID as APPLICATION_PROCESS_SESSION_ID;
74    pub use capi::PA_PROP_DEVICE_STRING as DEVICE_STRING;
75    pub use capi::PA_PROP_DEVICE_API as DEVICE_API;
76    pub use capi::PA_PROP_DEVICE_DESCRIPTION as DEVICE_DESCRIPTION;
77    pub use capi::PA_PROP_DEVICE_BUS_PATH as DEVICE_BUS_PATH;
78    pub use capi::PA_PROP_DEVICE_SERIAL as DEVICE_SERIAL;
79    pub use capi::PA_PROP_DEVICE_VENDOR_ID as DEVICE_VENDOR_ID;
80    pub use capi::PA_PROP_DEVICE_VENDOR_NAME as DEVICE_VENDOR_NAME;
81    pub use capi::PA_PROP_DEVICE_PRODUCT_ID as DEVICE_PRODUCT_ID;
82    pub use capi::PA_PROP_DEVICE_PRODUCT_NAME as DEVICE_PRODUCT_NAME;
83    pub use capi::PA_PROP_DEVICE_CLASS as DEVICE_CLASS;
84    pub use capi::PA_PROP_DEVICE_FORM_FACTOR as DEVICE_FORM_FACTOR;
85    pub use capi::PA_PROP_DEVICE_BUS as DEVICE_BUS;
86    pub use capi::PA_PROP_DEVICE_ICON as DEVICE_ICON;
87    pub use capi::PA_PROP_DEVICE_ICON_NAME as DEVICE_ICON_NAME;
88    pub use capi::PA_PROP_DEVICE_ACCESS_MODE as DEVICE_ACCESS_MODE;
89    pub use capi::PA_PROP_DEVICE_MASTER_DEVICE as DEVICE_MASTER_DEVICE;
90    pub use capi::PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE as DEVICE_BUFFERING_BUFFER_SIZE;
91    pub use capi::PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE as DEVICE_BUFFERING_FRAGMENT_SIZE;
92    pub use capi::PA_PROP_DEVICE_PROFILE_NAME as DEVICE_PROFILE_NAME;
93    pub use capi::PA_PROP_DEVICE_PROFILE_DESCRIPTION as DEVICE_PROFILE_DESCRIPTION;
94    pub use capi::PA_PROP_MODULE_AUTHOR as MODULE_AUTHOR;
95    pub use capi::PA_PROP_MODULE_DESCRIPTION as MODULE_DESCRIPTION;
96    pub use capi::PA_PROP_MODULE_USAGE as MODULE_USAGE;
97    pub use capi::PA_PROP_MODULE_VERSION as MODULE_VERSION;
98    pub use capi::PA_PROP_FORMAT_RATE as FORMAT_RATE;
99    pub use capi::PA_PROP_FORMAT_CHANNELS as FORMAT_CHANNELS;
100    #[cfg(any(doc, feature = "pa_v15"))]
101    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
102    pub use capi::PA_PROP_CONTEXT_FORCE_DISABLE_SHM as CONTEXT_FORCE_DISABLE_SHM;
103    #[cfg(any(doc, feature = "pa_v15"))]
104    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v15")))]
105    pub use capi::PA_PROP_BLUETOOTH_CODEC as BLUETOOTH_CODEC;
106
107    /* These need defining here, rather than `pub use`, in order to correctly link to other things
108     * in their doc comments */
109
110    /// For streams: the name of a filter that is desired, e.g. “echo-cancel” or “equalizer-sink”.
111    /// Differs from [`FILTER_WANT`] in that it forces PulseAudio to apply the filter, regardless of
112    /// whether PulseAudio thinks it makes sense to do so or not. If this is set, [`FILTER_WANT`] is
113    /// ignored. In other words, you almost certainly do not want to use this.
114    pub const FILTER_APPLY: &str = capi::PA_PROP_FILTER_APPLY;
115
116    /// For streams: the name of a filter that should specifically be suppressed (i.e. overrides
117    /// [`FILTER_WANT`]). Useful for the times that [`FILTER_WANT`] is automatically added (e.g.
118    /// echo-cancellation for phone streams when $VOIP_APP does its own, internal AEC).
119    pub const FILTER_SUPPRESS: &str = capi::PA_PROP_FILTER_SUPPRESS;
120
121    /// For devices: intended use. A space separated list of roles (see [`MEDIA_ROLE`]) this device
122    /// is particularly well suited for, due to latency, quality or form factor.
123    pub const DEVICE_INTENDED_ROLES: &str = capi::PA_PROP_DEVICE_INTENDED_ROLES;
124
125    /// For PCM formats: the sample format used as returned by
126    /// [`Format::to_string()`](crate::sample::Format::to_string).
127    pub const FORMAT_SAMPLE_FORMAT: &str = capi::PA_PROP_FORMAT_SAMPLE_FORMAT;
128
129    /// For PCM formats: the channel map of the stream as returned by
130    /// [`Map::print()`](crate::channelmap::Map::print).
131    pub const FORMAT_CHANNEL_MAP: &str = capi::PA_PROP_FORMAT_CHANNEL_MAP;
132}
133
134/// A property list object. Basically a dictionary with ASCII strings as keys and arbitrary data as
135/// values.
136pub struct Proplist(pub(crate) ProplistInner);
137
138unsafe impl Send for Proplist {}
139unsafe impl Sync for Proplist {}
140
141/// Inner type holding ownership over actual C object, necessary to guard against use-after-free
142/// issues with respect to the related `Iterator` object.
143pub(crate) struct ProplistInner {
144    /// The actual C object.
145    pub(crate) ptr: *mut ProplistInternal,
146    /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks.
147    weak: bool,
148}
149
150impl std::fmt::Debug for Proplist {
151    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
152        write!(f, "[{}]", self.to_string_sep(", ").unwrap())
153    }
154}
155
156/// Proplist iterator, used for iterating over the list’s keys. Returned by the
157/// [`Proplist::iter()`] method.
158///
159/// Note, lifetime `'a` is used to tie an instance of this struct to the associated `Proplist`, and
160/// thus prevent a use-after-free issue that would otherwise occur should the `Proplist` be
161/// destroyed first. Conversion from a `Proplist` via `into_iter` is okay though as responsibility
162/// for destruction is transfered to it.
163//XXX: Do **NOT** derive `Clone` for this, it will introduce a use-afer-free. To implement `Clone` properly would require an `Rc` wrapper around `ProplistInner`, but then if that would apply to `Proplist` also, that affects the `Send`+`Sync` properties of `Proplist` and anything using it.
164pub struct Iterator<'a> {
165    /// The actual C proplist object.
166    pl_ref: ProplistInner,
167    /// State tracker, used by underlying C function.
168    state: *mut c_void,
169    /// Use lifetime `'a`.
170    phantom: PhantomData<&'a ProplistInner>,
171}
172
173impl Iterator<'_> {
174    fn new(pl: *mut ProplistInternal) -> Self {
175        Self {
176            pl_ref: ProplistInner { ptr: pl, weak: true },
177            state: null_mut::<c_void>(),
178            phantom: PhantomData,
179        }
180    }
181}
182
183impl std::iter::Iterator for Iterator<'_> {
184    type Item = String;
185    fn next(&mut self) -> Option<Self::Item> {
186        let state_actual = &mut self.state as *mut *mut c_void;
187        let key_ptr = unsafe { capi::pa_proplist_iterate(self.pl_ref.ptr, state_actual) };
188        if key_ptr.is_null() {
189            return None;
190        }
191        // We assume key_ptr will never be null at this point
192        Some(unsafe { CStr::from_ptr(key_ptr).to_string_lossy().into_owned() })
193    }
194}
195
196impl IntoIterator for Proplist {
197    type Item = String;
198    type IntoIter = Iterator<'static>;
199
200    fn into_iter(mut self) -> Self::IntoIter {
201        assert_eq!(false, self.0.weak);
202        let mut iter = Iterator::new(self.0.ptr);
203        // Move responsibility for destruction
204        iter.pl_ref.weak = false;
205        self.0.weak = true;
206        iter
207    }
208}
209
210impl PartialEq for Proplist {
211    #[inline]
212    fn eq(&self, other: &Self) -> bool {
213        unsafe { capi::pa_proplist_equal(self.0.ptr, other.0.ptr) != 0 }
214    }
215}
216
217impl Proplist {
218    /// Allocates a property list.
219    pub fn new() -> Option<Self> {
220        let ptr = unsafe { capi::pa_proplist_new() };
221        match ptr.is_null() {
222            false => Some(Self::from_raw(ptr)),
223            true => None,
224        }
225    }
226
227    /// Allocates a new property list and assigns key/value from a human readable string.
228    pub fn new_from_string(s: &str) -> Option<Self> {
229        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
230        // as_ptr() giving dangling pointers!
231        let c_str = CString::new(s).unwrap();
232        let ptr = unsafe { capi::pa_proplist_from_string(c_str.as_ptr()) };
233        match ptr.is_null() {
234            false => Some(Self::from_raw(ptr)),
235            true => None,
236        }
237    }
238
239    /// Creates a new `Proplist` from an existing [`ProplistInternal`] pointer.
240    #[inline]
241    pub(crate) fn from_raw(ptr: *mut ProplistInternal) -> Self {
242        assert_eq!(false, ptr.is_null());
243        Proplist(ProplistInner { ptr: ptr, weak: false })
244    }
245
246    /// Creates a new `Proplist` from an existing [`ProplistInternal`] pointer.
247    ///
248    /// This is the ‘weak’ version, which avoids destroying the internal object when dropped.
249    #[inline]
250    pub(crate) fn from_raw_weak(ptr: *mut ProplistInternal) -> Self {
251        assert_eq!(false, ptr.is_null());
252        Proplist(ProplistInner { ptr: ptr, weak: true })
253    }
254
255    /// Allocates a new property list and copies over every single entry from the specified list.
256    ///
257    /// If this is called on a ‘weak’ instance, a non-weak object is returned.
258    #[inline]
259    pub(crate) fn to_owned(&self) -> Self {
260        Self::from_raw(unsafe { capi::pa_proplist_copy(self.0.ptr) })
261    }
262
263    /// Checks if the key is valid.
264    pub fn key_is_valid(key: &str) -> bool {
265        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
266        // as_ptr() giving dangling pointers!
267        let c_key = CString::new(key).unwrap();
268        unsafe { capi::pa_proplist_key_valid(c_key.as_ptr()) != 0 }
269    }
270
271    /// Appends a new string entry to the property list, possibly overwriting an already existing
272    /// entry with the same key.
273    ///
274    /// An internal copy is made of the provided string.
275    pub fn set_str(&mut self, key: &str, value: &str) -> Result<(), ()> {
276        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
277        // as_ptr() giving dangling pointers!
278        let c_key = CString::new(key).unwrap();
279        let c_value = CString::new(value).unwrap();
280        match unsafe { capi::pa_proplist_sets(self.0.ptr, c_key.as_ptr(), c_value.as_ptr()) } {
281            0 => Ok(()),
282            _ => Err(()),
283        }
284    }
285
286    /// Appends a new string entry to the property list, possibly overwriting an already existing
287    /// entry with the same key.
288    ///
289    /// This is similar to [`set_str()`](Self::set_str), however here the provided key and value
290    /// are combined into a single string, separated by an `=`. An internal copy is made of the
291    /// provided string.
292    pub fn set_pl(&mut self, pair: &str) -> Result<(), ()> {
293        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
294        // as_ptr() giving dangling pointers!
295        let c_pair = CString::new(pair).unwrap();
296        match unsafe { capi::pa_proplist_setp(self.0.ptr, c_pair.as_ptr()) } {
297            0 => Ok(()),
298            _ => Err(()),
299        }
300    }
301
302    /// Appends a new arbitrary data entry to the property list, possibly overwriting an already
303    /// existing entry with the same key.
304    ///
305    /// An internal copy of the provided data is made.
306    pub fn set(&mut self, key: &str, data: &[u8]) -> Result<(), ()> {
307        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
308        //  as_ptr() giving dangling pointers!
309        let c_key = CString::new(key).unwrap();
310        match unsafe { capi::pa_proplist_set(self.0.ptr, c_key.as_ptr(),
311            data.as_ptr() as *mut c_void, data.len()) }
312        {
313            0 => Ok(()),
314            _ => Err(()),
315        }
316    }
317
318    /// Gets a string entry for the specified key.
319    ///
320    /// Will return `None` if the key does not exist or if data is not valid UTF-8.
321    pub fn get_str(&self, key: &str) -> Option<String> {
322        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
323        // as_ptr() giving dangling pointers!
324        let c_key = CString::new(key).unwrap();
325        let ptr = unsafe { capi::pa_proplist_gets(self.0.ptr, c_key.as_ptr()) };
326        match ptr.is_null() {
327            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
328            true => None,
329        }
330    }
331
332    /// Gets the value for the specified key.
333    ///
334    /// For string entries, the value store will be NUL-terminated.
335    ///
336    /// The caller should make a copy of the data before any subsequent modification or destruction
337    /// of the property list.
338    ///
339    /// Returns a slice formed from the data pointer and the length of the data.
340    /// Returns `None` if key does not exist.
341    pub fn get(&self, key: &str) -> Option<&[u8]> {
342        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
343        // as_ptr() giving dangling pointers!
344        let c_key = CString::new(key).unwrap();
345        let mut data_ptr = null::<c_void>();
346        let mut nbytes: usize = 0;
347        if unsafe { capi::pa_proplist_get(self.0.ptr, c_key.as_ptr(), &mut data_ptr, &mut nbytes) }
348            != 0
349        {
350            return None;
351        }
352        if data_ptr.is_null() {
353            return None;
354        }
355        Some(unsafe { std::slice::from_raw_parts(data_ptr as *const u8, nbytes) })
356    }
357
358    /// Merges property list “other” into self, adhering to the merge mode specified.
359    #[inline]
360    pub fn merge(&mut self, other: &Self, mode: UpdateMode) {
361        unsafe { capi::pa_proplist_update(self.0.ptr, mode, other.0.ptr); }
362    }
363
364    /// Removes a single entry from the property list, identified by the specified key name.
365    pub fn unset(&mut self, key: &str) -> Result<(), PAErr> {
366        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
367        // as_ptr() giving dangling pointers!
368        let c_key = CString::new(key).unwrap();
369        match unsafe { capi::pa_proplist_unset(self.0.ptr, c_key.as_ptr()) } {
370            0 => Ok(()),
371            e => Err(PAErr(e)),
372        }
373    }
374
375    /// Similar to [`unset()`](Self::unset) but takes an array of keys to remove.
376    ///
377    /// Returns `None` on failure, otherwise the number of entries actually removed (which might
378    /// even be 0, if there were no matching entries to remove).
379    pub fn unset_many(&mut self, keys: &[&str]) -> Option<u32> {
380        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
381        // as_ptr() giving dangling pointers!
382        let mut c_keys: Vec<CString> = Vec::with_capacity(keys.len());
383        for k in keys {
384            c_keys.push(CString::new(*k).unwrap());
385        }
386
387        // Capture array of pointers to the above CString values.
388        // We also add a NULL pointer entry on the end, as expected by the C function called here.
389        let mut c_keys_ptrs: Vec<*const c_char> = Vec::with_capacity(c_keys.len() + 1);
390        for k in &c_keys {
391            c_keys_ptrs.push(k.as_ptr());
392        }
393        c_keys_ptrs.push(null());
394
395        match unsafe { capi::pa_proplist_unset_many(self.0.ptr, c_keys_ptrs.as_ptr()) } {
396            r if r < 0 => None,
397            r => Some(r as u32),
398        }
399    }
400
401    /// Gets an immutable iterator over the list’s keys.
402    ///
403    /// The property list should not be modified during iteration through the list, with the
404    /// exception of deleting the current entry. The keys in the property list do not have any
405    /// particular order.
406    ///
407    /// ```rust
408    /// # extern crate libpulse_binding as pulse;
409    /// # use pulse::proplist::Proplist;
410    /// #
411    /// # fn main() {
412    /// #     let mut my_props = Proplist::new().unwrap();
413    /// #     my_props.set_str(pulse::proplist::properties::APPLICATION_NAME, "FooApp").unwrap();
414    /// #
415    /// for key in my_props.iter() {
416    ///     //do something with it
417    ///     println!("key: {}", key);
418    /// }
419    /// # }
420    /// ```
421    #[inline]
422    pub fn iter(&self) -> Iterator<'_> {
423        Iterator::new(self.0.ptr)
424    }
425
426    /// Formats the property list nicely as a human readable string.
427    ///
428    /// This works very much like [`to_string_sep()`](Self::to_string_sep) and uses a newline as
429    /// separator and appends one final one.
430    pub fn to_string(&self) -> Option<String> {
431        let ptr = unsafe { capi::pa_proplist_to_string(self.0.ptr) };
432        if ptr.is_null() {
433            return None;
434        }
435        // Note, copying string on behalf of user here, and freeing that returned by PA, as
436        // documentation instructs, saving the user from having to remember.
437        unsafe {
438            let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
439            capi::pa_xfree(ptr as *mut c_void);
440            ret
441        }
442    }
443
444    /// Formats the property list nicely as a human readable string, choosing the separator used.
445    pub fn to_string_sep(&self, sep: &str) -> Option<String> {
446        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
447        // as_ptr() giving dangling pointers!
448        let c_sep = CString::new(sep).unwrap();
449        let ptr = unsafe { capi::pa_proplist_to_string_sep(self.0.ptr, c_sep.as_ptr()) };
450        if ptr.is_null() {
451            return None;
452        }
453        // Note, copying string on behalf of user here, and freeing that returned by PA, as
454        // documentation instructs, saving the user from having to remember.
455        unsafe {
456            let ret = Some(CStr::from_ptr(ptr).to_string_lossy().into_owned());
457            capi::pa_xfree(ptr as *mut c_void);
458            ret
459        }
460    }
461
462    /// Checks if this contains an entry with the given key.
463    ///
464    /// Returns `true` if an entry for the specified key exists in the property list. Returns `None`
465    /// on error.
466    pub fn contains(&self, key: &str) -> Option<bool> {
467        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
468        // as_ptr() giving dangling pointers!
469        let c_key = CString::new(key).unwrap();
470        match unsafe { capi::pa_proplist_contains(self.0.ptr, c_key.as_ptr()) } {
471            0 => Some(false),
472            1 => Some(true),
473            _ => None,
474        }
475    }
476
477    /// Removes all entries from the property list object.
478    #[inline]
479    pub fn clear(&mut self) {
480        unsafe { capi::pa_proplist_clear(self.0.ptr); }
481    }
482
483    /// Gets the number of entries in the property list.
484    #[inline]
485    pub fn len(&self) -> u32 {
486        unsafe { capi::pa_proplist_size(self.0.ptr) }
487    }
488
489    /// Checks if the proplist is empty.
490    #[inline]
491    pub fn is_empty(&self) -> bool {
492        unsafe { capi::pa_proplist_isempty(self.0.ptr) == 0 }
493    }
494}
495
496impl Drop for ProplistInner {
497    fn drop(&mut self) {
498        if !self.weak {
499            unsafe { capi::pa_proplist_free(self.ptr) };
500        }
501        self.ptr = null_mut::<ProplistInternal>();
502    }
503}
504
505impl Clone for Proplist {
506    /// Allocates a new property list and copy over every single entry from the specified list.
507    ///
508    /// If this is called on a ‘weak’ instance, a non-weak object is returned.
509    #[inline]
510    fn clone(&self) -> Self {
511        self.to_owned()
512    }
513}
514
515#[cfg(test)]
516mod tests {
517    use super::*;
518/*
519    /// Test that you cannot create a use-after-free situation by destroying a `Proplist` before an
520    /// associated `Iterator` (we avoid `Rc`/`Arc`).
521    #[test]
522    fn proplist_iter_lifetime() {
523        let iter = {
524            let my_props = Proplist::new().unwrap();
525            my_props.iter() //Returning this should not compile!
526        };
527
528        for key in iter {
529            //do something with it
530            println!("key: {}", key);
531        }
532    }
533*/
534    /// Test that you can however return an iterator if you convert the `Proplist` into one
535    #[test]
536    fn proplist_iter_lifetime_conv() {
537        let iter = {
538            let my_props = Proplist::new().unwrap();
539            my_props.into_iter()
540        };
541
542        for key in iter {
543            //do something with it
544            println!("key: {}", key);
545        }
546    }
547}