use crate::{error::Result, ffi, shared::*};
use std::{
ffi::{CStr, CString},
fmt::Debug,
os::raw::c_void,
ptr::{self, NonNull},
};
wrap_ref_mut!(AVDictionary: ffi::AVDictionary);
impl AVDictionary {
pub fn new(key: &CStr, value: &CStr, flags: u32) -> Self {
let mut dict = ptr::null_mut();
unsafe { ffi::av_dict_set(&mut dict, key.as_ptr(), value.as_ptr(), flags as i32) }
.upgrade()
.unwrap();
unsafe { Self::from_raw(NonNull::new(dict).unwrap()) }
}
pub fn new_int(key: &CStr, value: i64, flags: u32) -> Self {
let mut dict = ptr::null_mut();
unsafe { ffi::av_dict_set_int(&mut dict, key.as_ptr(), value, flags as i32) }
.upgrade()
.unwrap();
unsafe { Self::from_raw(NonNull::new(dict).unwrap()) }
}
pub fn from_string(
str: &CStr,
key_val_sep: &CStr,
pairs_sep: &CStr,
flags: u32,
) -> Option<Self> {
let mut dict = ptr::null_mut();
unsafe {
ffi::av_dict_parse_string(
&mut dict,
str.as_ptr(),
key_val_sep.as_ptr(),
pairs_sep.as_ptr(),
flags as i32,
)
}
.upgrade()
.ok()?;
Some(unsafe { Self::from_raw(NonNull::new(dict).unwrap()) })
}
pub fn set(mut self, key: &CStr, value: &CStr, flags: u32) -> Self {
let mut dict = self.as_mut_ptr();
unsafe { ffi::av_dict_set(&mut dict, key.as_ptr(), value.as_ptr(), flags as i32) }
.upgrade()
.unwrap();
unsafe { self.set_ptr(NonNull::new(dict).unwrap()) };
self
}
pub fn set_int(mut self, key: &CStr, value: i64, flags: u32) -> Self {
let mut dict = self.as_mut_ptr();
unsafe { ffi::av_dict_set_int(&mut dict, key.as_ptr(), value, flags as i32) }
.upgrade()
.unwrap();
unsafe { self.set_ptr(NonNull::new(dict).unwrap()) };
self
}
pub fn parse_string(
mut self,
str: &CStr,
key_val_sep: &CStr,
pairs_sep: &CStr,
flags: u32,
) -> Result<Self> {
let mut dict = self.as_mut_ptr();
unsafe {
ffi::av_dict_parse_string(
&mut dict,
str.as_ptr(),
key_val_sep.as_ptr(),
pairs_sep.as_ptr(),
flags as i32,
)
}
.upgrade()?;
unsafe { self.set_ptr(NonNull::new(dict).unwrap()) };
Ok(self)
}
pub fn copy(mut self, another: &AVDictionary, flags: u32) -> Self {
let mut dict = self.as_mut_ptr();
unsafe { ffi::av_dict_copy(&mut dict, another.as_ptr(), flags as i32) }
.upgrade()
.unwrap();
unsafe { self.set_ptr(NonNull::new(dict).unwrap()) };
self
}
pub fn get_string(&self, key_val_sep: u8, pairs_sep: u8) -> Result<CString> {
let mut s = ptr::null_mut();
unsafe { ffi::av_dict_get_string(self.as_ptr(), &mut s, key_val_sep as _, pairs_sep as _) }
.upgrade()?;
let result = unsafe { CStr::from_ptr(s).to_owned() };
unsafe {
ffi::av_freep(&mut s as *mut _ as *mut c_void);
}
Ok(result)
}
}
impl<'dict> AVDictionary {
pub fn get(
&'dict self,
key: &CStr,
prev: Option<AVDictionaryEntryRef>,
flags: u32,
) -> Option<AVDictionaryEntryRef<'dict>> {
let prev_ptr = match prev {
Some(entry) => entry.as_ptr(),
None => ptr::null(),
};
unsafe { ffi::av_dict_get(self.as_ptr(), key.as_ptr(), prev_ptr, flags as i32) }
.upgrade()
.map(|ptr| unsafe { AVDictionaryEntryRef::from_raw(ptr) })
}
pub fn iter(&'dict self) -> AVDictionaryIter<'dict> {
AVDictionaryIter {
dict: self,
ptr: ptr::null(),
_phantom: std::marker::PhantomData,
}
}
}
impl Clone for AVDictionary {
fn clone(&self) -> Self {
let mut newer = ptr::null_mut();
unsafe { ffi::av_dict_copy(&mut newer, self.as_ptr(), 0) }
.upgrade()
.unwrap();
unsafe { Self::from_raw(NonNull::new(newer).unwrap()) }
}
}
impl Drop for AVDictionary {
fn drop(&mut self) {
let mut dict = self.as_mut_ptr();
unsafe { ffi::av_dict_free(&mut dict) }
}
}
impl<'dict> IntoIterator for &'dict AVDictionary {
type IntoIter = AVDictionaryIter<'dict>;
type Item = AVDictionaryEntryRef<'dict>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl Debug for AVDictionary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut out = f.debug_map();
for entry in self.into_iter() {
out.entry(&entry.key(), &entry.value());
}
out.finish()
}
}
pub struct AVDictionaryIter<'dict> {
dict: &'dict AVDictionary,
ptr: *const ffi::AVDictionaryEntry,
_phantom: std::marker::PhantomData<&'dict ()>,
}
impl<'dict> Iterator for AVDictionaryIter<'dict> {
type Item = AVDictionaryEntryRef<'dict>;
fn next(&mut self) -> Option<Self::Item> {
self.ptr = unsafe { ffi::av_dict_iterate(self.dict.as_ptr(), self.ptr) };
self.ptr
.upgrade()
.map(|x| unsafe { AVDictionaryEntryRef::from_raw(x) })
}
}
wrap_ref_mut!(AVDictionaryEntry: ffi::AVDictionaryEntry);
impl AVDictionaryEntry {
pub fn key(&self) -> &CStr {
unsafe { CStr::from_ptr(self.key) }
}
pub fn value(&self) -> &CStr {
unsafe { CStr::from_ptr(self.value) }
}
}
#[cfg(test)]
mod test {
use super::AVDictionary;
#[test]
fn set() {
let _ = AVDictionary::new(c"bob", c"alice", 0);
let _ = AVDictionary::new(c"bob", c"alice", 0)
.set(c"a;dsjfadsfa", c"asdfjal;sdfj", 0)
.set(c"foo", c"bar", 0);
let _ = AVDictionary::new(c"bob", c"alice", 0).set(c"bob", c"alice", 0);
}
#[test]
fn set_int() {
let dict = AVDictionary::new_int(c"bob", 2233, 0).set_int(c"foo", 123456789123456789, 0);
assert_eq!(
c"123456789123456789".as_ref(),
dict.get(c"foo", None, 0).unwrap().value()
);
}
#[test]
fn get() {
let dict = AVDictionary::new(c"bob", c"alice", 0);
assert_eq!(
c"alice".as_ref(),
dict.get(c"bob", None, 0).unwrap().value()
);
let dict = AVDictionary::new(c"bob", c"alice", 0)
.set(c"bob", c"alice", 0)
.set(c"bob", c"alice", 0)
.set(c"bob", c"alice", 0)
.set(c"bob", c"alice", 0);
assert_eq!(
c"alice".as_ref(),
dict.get(c"bob", None, 0).unwrap().value()
);
let dict = AVDictionary::new(c"foo", c"bar", 0).set(c"bob", c"alice", 0);
assert_eq!(c"bar".as_ref(), dict.get(c"foo", None, 0).unwrap().value());
assert_eq!(
c"alice".as_ref(),
dict.get(c"bob", None, 0).unwrap().value()
);
let entry = dict.get(c"bob", None, 0).unwrap();
assert_eq!(c"alice".as_ref(), entry.value());
assert!(dict.get(c"foo", Some(entry), 0).is_none());
let dict = AVDictionary::new(c"bob", c"alice0", 0)
.set(c"bob", c"alice1", 0)
.set(c"bob", c"alice2", 0)
.set(c"bob", c"alice3", 0)
.set(c"bob", c"alice4", 0);
let entry = dict.get(c"bob", None, 0).unwrap();
assert_eq!(c"alice4".as_ref(), entry.value());
assert_eq!(c"bob".as_ref(), entry.key());
assert!(dict.get(c"bob", Some(entry), 0).is_none());
}
#[test]
fn copy() {
let dicta = AVDictionary::new(c"a", c"b", 0).set(c"c", c"d", 0);
let dictc = dicta.clone();
assert_eq!(c"a:b-c:d", dictc.get_string(b':', b'-').unwrap().as_c_str());
let dictb = AVDictionary::new(c"foo", c"bar", 0)
.set(c"alice", c"bob", 0)
.copy(&dictc, 0);
assert_eq!(
c"foo:bar-alice:bob-a:b-c:d",
dictb.get_string(b':', b'-').unwrap().as_c_str(),
);
let dicta = dicta.set(c"e", c"f", 0);
assert_eq!(c"b".as_ref(), dicta.get(c"a", None, 0).unwrap().value());
assert_eq!(c"d".as_ref(), dicta.get(c"c", None, 0).unwrap().value());
assert_eq!(c"f".as_ref(), dicta.get(c"e", None, 0).unwrap().value());
assert_eq!(c"b".as_ref(), dictb.get(c"a", None, 0).unwrap().value());
assert_eq!(c"d".as_ref(), dictb.get(c"c", None, 0).unwrap().value());
assert!(dictb.get(c"e", None, 0).is_none());
}
#[test]
fn serialization() {
let dict = AVDictionary::new(c"a", c"b", 0)
.set(c"c", c"d", 0)
.set(c"foo", c"bar", 0)
.set(c"bob", c"alice", 0);
assert_eq!(
c"a:b-c:d-foo:bar-bob:alice",
dict.get_string(b':', b'-').unwrap().as_c_str()
);
let dict = dict.set(c"rust", c"c", 0);
assert_eq!(
c"a:b-c:d-foo:bar-bob:alice-rust:c",
dict.get_string(b':', b'-').unwrap().as_c_str()
);
}
#[test]
fn deserialization() {
let dict =
AVDictionary::from_string(c"a:b-c:d-foo:bar-bob:alice-rust:c", c":", c"-", 0).unwrap();
assert_eq!(
c"a:b-c:d-foo:bar-bob:alice-rust:c",
dict.get_string(b':', b'-').unwrap().as_c_str()
);
}
}