1use bitflags::bitflags;
7pub use spa_sys::spa_dict_item;
9use std::{convert::TryInto, ffi::CStr, fmt, marker::PhantomData, ptr};
10
11#[repr(transparent)]
12pub struct DictRef(spa_sys::spa_dict);
13
14impl DictRef {
15 pub fn as_raw(&self) -> &spa_sys::spa_dict {
17 &self.0
18 }
19
20 pub fn as_raw_ptr(&self) -> *mut spa_sys::spa_dict {
25 self.as_raw() as *const _ as *mut _
26 }
27
28 pub fn iter_cstr(&self) -> CIter<'_> {
32 let items = if self.0.items.is_null() {
33 &[]
34 } else {
35 unsafe { std::slice::from_raw_parts(self.0.items, self.len()) }
36 };
37
38 CIter {
39 items,
40 _phantom: PhantomData,
41 }
42 }
43
44 pub fn iter(&self) -> Iter<'_> {
47 Iter {
48 inner: self.iter_cstr(),
49 }
50 }
51
52 pub fn keys(&self) -> Keys<'_> {
55 Keys {
56 inner: self.iter_cstr(),
57 }
58 }
59
60 pub fn values(&self) -> Values<'_> {
63 Values {
64 inner: self.iter_cstr(),
65 }
66 }
67
68 pub fn len(&self) -> usize {
71 self.0.n_items.try_into().unwrap()
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78
79 pub fn flags(&self) -> Flags {
81 Flags::from_bits_retain(self.0.flags)
82 }
83
84 pub fn get(&self, key: &str) -> Option<&str> {
91 self.iter().find(|(k, _)| *k == key).map(|(_, v)| v)
92 }
93
94 pub fn parse<T: ParsableValue>(&self, key: &str) -> Option<Result<T, ParseValueError>> {
123 self.iter()
124 .find(|(k, _)| *k == key)
125 .map(|(_, v)| match T::parse_value(v) {
126 Some(v) => Ok(v),
127 None => Err(ParseValueError {
128 value: v.to_string(),
129 type_name: std::any::type_name::<T>(),
130 }),
131 })
132 }
133}
134
135impl AsRef<Self> for DictRef {
136 fn as_ref(&self) -> &Self {
137 self
138 }
139}
140
141impl std::fmt::Debug for DictRef {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 struct Entries<'a>(CIter<'a>);
144
145 impl<'a> fmt::Debug for Entries<'a> {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.debug_map().entries(self.0.clone()).finish()
148 }
149 }
150
151 f.debug_struct("DictRef")
152 .field("flags", &self.flags())
153 .field("entries", &Entries(self.iter_cstr()))
154 .finish()
155 }
156}
157
158#[derive(Debug, Eq, PartialEq)]
160pub struct ParseValueError {
161 value: String,
162 type_name: &'static str,
163}
164
165impl std::error::Error for ParseValueError {}
166
167impl fmt::Display for ParseValueError {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 write!(f, "'{}' cannot be parsed to {}", self.value, self.type_name)
170 }
171}
172
173pub trait ParsableValue: Copy {
175 fn parse_value(value: &str) -> Option<Self>;
177}
178
179impl ParsableValue for bool {
180 fn parse_value(value: &str) -> Option<Self> {
181 if value == "true" {
183 Some(true)
184 } else {
185 match value.parse::<i32>() {
186 Ok(1) => Some(true),
187 _ => Some(false),
188 }
189 }
190 }
191}
192
193macro_rules! impl_parsable_value_numeric {
194 ($type_:ty) => {
195 impl ParsableValue for $type_ {
196 fn parse_value(value: &str) -> Option<Self> {
197 value.parse().ok()
198 }
199 }
200 };
201}
202
203impl_parsable_value_numeric!(i32);
204impl_parsable_value_numeric!(i64);
205impl_parsable_value_numeric!(u64);
206impl_parsable_value_numeric!(f32);
207impl_parsable_value_numeric!(f64);
208impl_parsable_value_numeric!(i8);
210impl_parsable_value_numeric!(u8);
211impl_parsable_value_numeric!(i16);
212impl_parsable_value_numeric!(u16);
213impl_parsable_value_numeric!(u32);
214impl_parsable_value_numeric!(i128);
215impl_parsable_value_numeric!(u128);
216impl_parsable_value_numeric!(isize);
217impl_parsable_value_numeric!(usize);
218
219const POINTER_PREFIX: &str = "pointer:0x";
220
221impl<T> ParsableValue for *const T {
222 fn parse_value(value: &str) -> Option<Self> {
223 match value
224 .strip_prefix(POINTER_PREFIX)
225 .map(|addr| usize::from_str_radix(addr, 16))
226 {
227 Some(Ok(addr)) => Some(addr as *const T),
228 _ => None,
229 }
230 }
231}
232
233bitflags! {
234 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
236 pub struct Flags: u32 {
237 const SORTED = spa_sys::SPA_DICT_FLAG_SORTED;
241 }
242}
243
244#[derive(Clone)]
246pub struct CIter<'a> {
247 items: &'a [spa_sys::spa_dict_item],
248 _phantom: PhantomData<&'a str>,
249}
250
251impl<'a> Iterator for CIter<'a> {
252 type Item = (&'a CStr, &'a CStr);
253
254 fn next(&mut self) -> Option<Self::Item> {
255 self.items.split_first().map(|(item, rest)| {
256 self.items = rest;
257 let k = unsafe { CStr::from_ptr(item.key) };
258 let v = unsafe { CStr::from_ptr(item.value) };
259 (k, v)
260 })
261 }
262
263 fn size_hint(&self) -> (usize, Option<usize>) {
264 let bound = self.items.len();
265 (bound, Some(bound))
267 }
268}
269
270pub struct Iter<'a> {
272 inner: CIter<'a>,
273}
274
275impl<'a> Iterator for Iter<'a> {
276 type Item = (&'a str, &'a str);
277
278 fn next(&mut self) -> Option<Self::Item> {
279 self.inner
280 .find_map(|(k, v)| k.to_str().ok().zip(v.to_str().ok()))
281 }
282
283 fn size_hint(&self) -> (usize, Option<usize>) {
284 (0, self.inner.size_hint().1)
286 }
287}
288
289pub struct Keys<'a> {
291 inner: CIter<'a>,
292}
293
294impl<'a> Iterator for Keys<'a> {
295 type Item = &'a str;
296
297 fn next(&mut self) -> Option<Self::Item> {
298 self.inner.find_map(|(k, _)| k.to_str().ok())
299 }
300
301 fn size_hint(&self) -> (usize, Option<usize>) {
302 self.inner.size_hint()
303 }
304}
305
306pub struct Values<'a> {
308 inner: CIter<'a>,
309}
310
311impl<'a> Iterator for Values<'a> {
312 type Item = &'a str;
313
314 fn next(&mut self) -> Option<Self::Item> {
315 self.inner.find_map(|(_, v)| v.to_str().ok())
316 }
317
318 fn size_hint(&self) -> (usize, Option<usize>) {
319 self.inner.size_hint()
320 }
321}
322
323pub struct StaticDict {
339 ptr: ptr::NonNull<spa_sys::spa_dict>,
340}
341
342impl StaticDict {
343 pub const unsafe fn from_ptr(ptr: ptr::NonNull<spa_sys::spa_dict>) -> Self {
350 Self { ptr }
351 }
352}
353
354#[macro_export]
369macro_rules! static_dict {
370 {$($k:expr => $v:expr),+ $(,)?} => {{
371 use $crate::utils::dict::{spa_dict_item, StaticDict, Flags};
372 use std::ptr;
373
374 static mut ITEMS: *const [spa_dict_item] = &[
375 $(
376 spa_dict_item {
377 key: concat!($k, "\0").as_ptr() as *const std::os::raw::c_char,
378 value: concat!($v, "\0").as_ptr() as *const std::os::raw::c_char
379 },
380 )+
381 ];
382
383 static mut RAW: spa_sys::spa_dict = unsafe {
384 spa_sys::spa_dict {
385 flags: Flags::empty().bits(),
386 n_items: ITEMS.len() as u32,
387 items: ITEMS as *const spa_dict_item,
388 }
389 };
390
391 unsafe {
392 let ptr = std::ptr::addr_of!(RAW).cast_mut();
393 StaticDict::from_ptr(ptr::NonNull::new_unchecked(ptr))
394 }
395 }};
396}
397
398impl std::ops::Deref for StaticDict {
399 type Target = DictRef;
400
401 fn deref(&self) -> &Self::Target {
402 unsafe { self.ptr.cast::<Self::Target>().as_ref() }
403 }
404}
405
406impl fmt::Debug for StaticDict {
407 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408 let dict: &DictRef = self.as_ref();
409 f.debug_tuple("StaticDict").field(dict).finish()
411 }
412}
413
414unsafe impl Send for StaticDict {}
415unsafe impl Sync for StaticDict {}
416
417#[cfg(test)]
418mod tests {
419 use super::{DictRef, Flags, StaticDict};
420 use spa_sys::spa_dict;
421 use std::ptr;
422
423 #[test]
424 fn test_empty_dict() {
425 let raw = spa_dict {
426 flags: Flags::empty().bits(),
427 n_items: 0,
428 items: ptr::null(),
429 };
430
431 let dict = DictRef(raw);
432 let iter = dict.iter_cstr();
433
434 assert_eq!(0, dict.len());
435
436 iter.for_each(|_| panic!("Iterated over non-existing item"));
437 }
438
439 #[test]
440 fn test_iter_cstr() {
441 let dict = static_dict! {
442 "K0" => "V0",
443 "K1" => "V1"
444 };
445
446 let mut iter = dict.iter_cstr();
447 assert_eq!((c"K0", c"V0"), iter.next().unwrap());
448 assert_eq!((c"K1", c"V1"), iter.next().unwrap());
449 assert_eq!(None, iter.next());
450 }
451
452 #[test]
453 fn test_iterators() {
454 let dict = static_dict! {
455 "K0" => "V0",
456 "K1" => "V1"
457 };
458
459 let mut iter = dict.iter();
460 assert_eq!(("K0", "V0"), iter.next().unwrap());
461 assert_eq!(("K1", "V1"), iter.next().unwrap());
462 assert_eq!(None, iter.next());
463
464 let mut key_iter = dict.keys();
465 assert_eq!("K0", key_iter.next().unwrap());
466 assert_eq!("K1", key_iter.next().unwrap());
467 assert_eq!(None, key_iter.next());
468
469 let mut val_iter = dict.values();
470 assert_eq!("V0", val_iter.next().unwrap());
471 assert_eq!("V1", val_iter.next().unwrap());
472 assert_eq!(None, val_iter.next());
473 }
474
475 #[test]
476 fn test_get() {
477 let dict = static_dict! {
478 "K0" => "V0"
479 };
480
481 assert_eq!(Some("V0"), dict.get("K0"));
482 }
483
484 #[test]
485 fn test_debug() {
486 let dict = static_dict! {
487 "K0" => "V0"
488 };
489
490 assert_eq!(
491 r#"StaticDict(DictRef { flags: Flags(0x0), entries: {"K0": "V0"} })"#,
492 &format!("{:?}", dict)
493 );
494
495 let raw = spa_dict {
496 flags: Flags::SORTED.bits(),
497 n_items: 0,
498 items: ptr::null(),
499 };
500
501 let dict = DictRef(raw);
502
503 assert_eq!(
504 r#"DictRef { flags: Flags(SORTED), entries: {} }"#,
505 &format!("{:?}", dict)
506 );
507 }
508
509 #[test]
510 fn static_dict() {
511 static DICT: StaticDict = static_dict! {
512 "K0" => "V0",
513 "K1" => "V1"
514 };
515
516 assert_eq!(DICT.len(), 2);
517 assert_eq!(DICT.get("K0"), Some("V0"));
518 assert_eq!(DICT.get("K1"), Some("V1"));
519 }
520
521 #[test]
522 fn parse() {
523 use super::ParseValueError;
524
525 static DICT: StaticDict = static_dict! {
526 "true" => "true",
527 "false" => "false",
528 "1" => "1",
529 "10" => "10",
530 "-10" => "-10",
531 "i64-max" => "9223372036854775807",
532 "1.5" => "1.5",
533 "-1.5" => "-1.5",
534 "pointer" => "pointer:0xdeadbeef",
535 "badger" => "badger"
536 };
537
538 macro_rules! parse_error {
539 ($key:literal, $type_:ty) => {
540 assert!(matches!(
541 DICT.parse::<$type_>($key),
542 Some(Err(ParseValueError { .. }))
543 ));
544 };
545 }
546
547 assert_eq!(DICT.parse::<bool>("missing"), None);
548
549 assert_eq!(DICT.parse("true"), Some(Ok(true)));
550 assert_eq!(DICT.parse("1"), Some(Ok(true)));
551 assert_eq!(DICT.parse("false"), Some(Ok(false)));
552 assert_eq!(DICT.parse("10"), Some(Ok(false)));
553 assert_eq!(DICT.parse("badger"), Some(Ok(false)));
554
555 assert_eq!(DICT.parse::<i32>("1"), Some(Ok(1)));
557 assert_eq!(DICT.parse::<i32>("-10"), Some(Ok(-10)));
558 parse_error!("badger", i32);
559 parse_error!("i64-max", i32);
560
561 assert_eq!(DICT.parse::<i64>("1"), Some(Ok(1)));
562 assert_eq!(DICT.parse::<i64>("-10"), Some(Ok(-10)));
563 assert_eq!(DICT.parse::<i64>("i64-max"), Some(Ok(i64::MAX)));
564 parse_error!("badger", i64);
565
566 assert_eq!(DICT.parse::<u64>("1"), Some(Ok(1)));
567 assert_eq!(DICT.parse::<u64>("i64-max"), Some(Ok(i64::MAX as u64)));
568 parse_error!("-10", u64);
569 parse_error!("badger", u64);
570
571 assert_eq!(DICT.parse::<i8>("1"), Some(Ok(1)));
572 assert_eq!(DICT.parse::<i8>("-10"), Some(Ok(-10)));
573
574 assert_eq!(DICT.parse::<u8>("1"), Some(Ok(1)));
575 parse_error!("-10", u8);
576
577 assert_eq!(DICT.parse::<i16>("1"), Some(Ok(1)));
578 assert_eq!(DICT.parse::<i16>("-10"), Some(Ok(-10)));
579
580 assert_eq!(DICT.parse::<u16>("1"), Some(Ok(1)));
581 parse_error!("-10", u16);
582
583 assert_eq!(DICT.parse::<u32>("1"), Some(Ok(1)));
584 parse_error!("-10", u32);
585
586 assert_eq!(DICT.parse::<i128>("1"), Some(Ok(1)));
587 assert_eq!(DICT.parse::<i128>("-10"), Some(Ok(-10)));
588
589 assert_eq!(DICT.parse::<u128>("1"), Some(Ok(1)));
590 parse_error!("-10", u128);
591
592 assert_eq!(DICT.parse::<isize>("1"), Some(Ok(1)));
593 assert_eq!(DICT.parse::<isize>("-10"), Some(Ok(-10)));
594
595 assert_eq!(DICT.parse::<usize>("1"), Some(Ok(1)));
596 parse_error!("-10", usize);
597
598 assert_eq!(DICT.parse::<f32>("1"), Some(Ok(1.0)));
600 assert_eq!(DICT.parse::<f32>("-10"), Some(Ok(-10.0)));
601 assert_eq!(DICT.parse::<f32>("1.5"), Some(Ok(1.5)));
602 assert_eq!(DICT.parse::<f32>("-1.5"), Some(Ok(-1.5)));
603 parse_error!("badger", f32);
604
605 assert_eq!(DICT.parse::<f64>("1"), Some(Ok(1.0)));
606 assert_eq!(DICT.parse::<f64>("-10"), Some(Ok(-10.0)));
607 assert_eq!(DICT.parse::<f64>("1.5"), Some(Ok(1.5)));
608 assert_eq!(DICT.parse::<f64>("-1.5"), Some(Ok(-1.5)));
609 parse_error!("badger", f64);
610
611 let ptr = DICT.parse::<*const i32>("pointer").unwrap().unwrap();
613 assert!(!ptr.is_null());
614 parse_error!("badger", *const i32);
615 }
616}