use std::{
ffi::{CStr, CString},
fmt,
mem::ManuallyDrop,
ops::Deref,
ptr,
};
pub struct Properties {
ptr: ptr::NonNull<pw_sys::pw_properties>,
}
#[macro_export]
macro_rules! __properties__ {
{$($k:expr => $v:expr),+ $(,)?} => {{
let mut properties = $crate::properties::Properties::new();
$(
properties.insert($k, $v);
)*
properties
}};
}
pub use __properties__ as properties;
impl Properties {
pub fn new() -> Self {
unsafe {
let raw = std::ptr::NonNull::new(pw_sys::pw_properties_new(std::ptr::null()))
.expect("Newly created pw_properties should not be null");
Self::from_ptr(raw)
}
}
pub unsafe fn from_ptr(ptr: ptr::NonNull<pw_sys::pw_properties>) -> Self {
Self { ptr }
}
pub fn into_raw(self) -> *mut pw_sys::pw_properties {
let this = ManuallyDrop::new(self);
this.ptr.as_ptr()
}
pub fn from_dict(dict: &spa::utils::dict::DictRef) -> Self {
let ptr = dict.as_raw();
unsafe {
let copy = pw_sys::pw_properties_new_dict(ptr);
Self::from_ptr(ptr::NonNull::new(copy).expect("pw_properties_new_dict() returned NULL"))
}
}
}
impl AsRef<PropertiesRef> for Properties {
fn as_ref(&self) -> &PropertiesRef {
self.deref()
}
}
impl AsRef<spa::utils::dict::DictRef> for Properties {
fn as_ref(&self) -> &spa::utils::dict::DictRef {
self.deref().as_ref()
}
}
impl std::ops::Deref for Properties {
type Target = PropertiesRef;
fn deref(&self) -> &Self::Target {
unsafe { self.ptr.cast().as_ref() }
}
}
impl std::ops::DerefMut for Properties {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.ptr.cast().as_mut() }
}
}
impl Default for Properties {
fn default() -> Self {
Self::new()
}
}
impl Clone for Properties {
fn clone(&self) -> Self {
unsafe {
let ptr = pw_sys::pw_properties_copy(self.as_raw_ptr());
let ptr = ptr::NonNull::new_unchecked(ptr);
Self { ptr }
}
}
}
impl<K, V> FromIterator<(K, V)> for Properties
where
K: Into<Vec<u8>>,
V: Into<Vec<u8>>,
{
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
let mut props = Self::new();
props.extend(iter);
props
}
}
impl Drop for Properties {
fn drop(&mut self) {
unsafe { pw_sys::pw_properties_free(self.ptr.as_ptr()) }
}
}
impl fmt::Debug for Properties {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let dict: &spa::utils::dict::DictRef = self.as_ref();
f.debug_tuple("Properties").field(dict).finish()
}
}
#[repr(transparent)]
pub struct PropertiesRef(pw_sys::pw_properties);
impl PropertiesRef {
pub fn as_raw(&self) -> &pw_sys::pw_properties {
&self.0
}
pub fn as_raw_ptr(&self) -> *mut pw_sys::pw_properties {
std::ptr::addr_of!(self.0).cast_mut()
}
pub fn dict(&self) -> &spa::utils::dict::DictRef {
unsafe { &*(&self.0.dict as *const spa_sys::spa_dict as *const spa::utils::dict::DictRef) }
}
pub fn to_owned(&self) -> Properties {
unsafe {
let ptr = pw_sys::pw_properties_copy(self.as_raw_ptr());
Properties::from_ptr(ptr::NonNull::new_unchecked(ptr))
}
}
pub fn get(&self, key: &str) -> Option<&str> {
let key = CString::new(key).expect("key contains null byte");
let key_cstr = key.as_c_str();
PropertiesRef::get_cstr(self, key_cstr)
}
pub fn get_cstr(&self, key: &CStr) -> Option<&str> {
let res =
unsafe { pw_sys::pw_properties_get(self.as_raw_ptr().cast_const(), key.as_ptr()) };
let res = if !res.is_null() {
unsafe { Some(std::ffi::CStr::from_ptr(res)) }
} else {
None
};
res.and_then(|res| res.to_str().ok())
}
pub fn insert<K, V>(&mut self, key: K, value: V)
where
K: Into<Vec<u8>>,
V: Into<Vec<u8>>,
{
let k = CString::new(key).unwrap();
let v = CString::new(value).unwrap();
unsafe { pw_sys::pw_properties_set(self.as_raw_ptr(), k.as_ptr(), v.as_ptr()) };
}
pub fn remove<T>(&mut self, key: T)
where
T: Into<Vec<u8>>,
{
let key = CString::new(key).unwrap();
unsafe { pw_sys::pw_properties_set(self.as_raw_ptr(), key.as_ptr(), std::ptr::null()) };
}
pub fn clear(&mut self) {
unsafe { pw_sys::pw_properties_clear(self.as_raw_ptr()) }
}
}
impl AsRef<spa::utils::dict::DictRef> for PropertiesRef {
fn as_ref(&self) -> &spa::utils::dict::DictRef {
self.dict()
}
}
impl fmt::Debug for PropertiesRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("PropertiesRef").field(self.as_ref()).finish()
}
}
impl<K, V> Extend<(K, V)> for PropertiesRef
where
K: Into<Vec<u8>>,
V: Into<Vec<u8>>,
{
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
for (k, v) in iter {
self.insert(k, v);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let props = properties! {
"K0" => "V0"
};
let mut iter = props.dict().iter();
assert_eq!(("K0", "V0"), iter.next().unwrap());
assert_eq!(None, iter.next());
}
#[test]
fn remove() {
let mut props = properties! {
"K0" => "V0"
};
assert_eq!(Some("V0"), props.dict().get("K0"));
props.remove("K0");
assert_eq!(None, props.dict().get("K0"));
}
#[test]
fn insert() {
let mut props = properties! {
"K0" => "V0"
};
assert_eq!(None, props.dict().get("K1"));
props.insert("K1", "V1");
assert_eq!(Some("V1"), props.dict().get("K1"));
}
#[test]
fn clone() {
let props1 = properties! {
"K0" => "V0"
};
let mut props2 = props1.clone();
props2.insert("K1", "V1");
assert_eq!(None, props1.dict().get("K1"));
assert_eq!(Some("V1"), props2.dict().get("K1"));
}
#[test]
fn from_dict() {
use spa::static_dict;
let mut props = {
let dict = static_dict! { "K0" => "V0" };
Properties::from_dict(&dict)
};
assert_eq!(props.dict().len(), 1);
assert_eq!(props.dict().get("K0"), Some("V0"));
props.insert("K1", "V1");
assert_eq!(props.dict().len(), 2);
assert_eq!(props.dict().get("K1"), Some("V1"));
}
#[test]
fn properties_ref() {
let props = properties! {
"K0" => "V0"
};
println!("{:?}", &props);
let props_ref: &PropertiesRef = props.deref();
assert_eq!(props_ref.dict().len(), 1);
assert_eq!(props_ref.dict().get("K0"), Some("V0"));
dbg!(&props_ref);
let props_copy = props_ref.to_owned();
assert_eq!(props_copy.dict().len(), 1);
assert_eq!(props_copy.dict().get("K0"), Some("V0"));
}
}