use std::collections::HashMap;
use std::ffi::{CStr, CString};
use ffmpeg_sys_next::*;
use crate::error::FfmpegError;
use crate::smart_object::SmartPtr;
pub struct Dictionary {
ptr: SmartPtr<AVDictionary>,
}
unsafe impl Send for Dictionary {}
impl Default for Dictionary {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for Dictionary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let dict = HashMap::<String, String>::from(self);
dict.fmt(f)
}
}
impl Clone for Dictionary {
fn clone(&self) -> Self {
let mut dict = Self::new();
Self::clone_from(&mut dict, self);
dict
}
fn clone_from(&mut self, source: &Self) {
let ret = unsafe { av_dict_copy(self.as_mut_ptr_ref(), source.as_ptr(), 0) };
if ret < 0 {
panic!("failed to clone dictionary: {ret}")
}
}
}
pub struct DictionaryBuilder {
dict: Dictionary,
}
impl DictionaryBuilder {
pub fn set(mut self, key: &str, value: &str) -> Self {
self.dict.set(key, value).expect("Failed to set dictionary entry");
self
}
pub fn build(self) -> Dictionary {
self.dict
}
}
impl Dictionary {
pub fn new() -> Self {
Self {
ptr: unsafe {
SmartPtr::wrap(std::ptr::null_mut(), |ptr| {
av_dict_free(ptr)
})
},
}
}
pub fn builder() -> DictionaryBuilder {
DictionaryBuilder { dict: Self::new() }
}
pub unsafe fn from_ptr_ref(ptr: *mut AVDictionary) -> Self {
Self {
ptr: SmartPtr::wrap(ptr as _, |_| {}),
}
}
pub unsafe fn from_ptr_owned(ptr: *mut AVDictionary) -> Self {
Self {
ptr: SmartPtr::wrap(ptr, |ptr| {
av_dict_free(ptr)
}),
}
}
pub fn set(&mut self, key: &str, value: &str) -> Result<(), FfmpegError> {
let key = CString::new(key).expect("Failed to convert key to CString");
let value = CString::new(value).expect("Failed to convert value to CString");
let ret = unsafe { av_dict_set(self.ptr.as_mut(), key.as_ptr(), value.as_ptr(), 0) };
if ret < 0 {
Err(FfmpegError::Code(ret.into()))
} else {
Ok(())
}
}
pub fn get(&self, key: &str) -> Option<String> {
let key = CString::new(key).expect("Failed to convert key to CString");
let entry = unsafe { av_dict_get(self.as_ptr(), key.as_ptr(), std::ptr::null_mut(), AV_DICT_IGNORE_SUFFIX) };
if entry.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr((*entry).value) }.to_string_lossy().into_owned())
}
}
pub fn iter(&self) -> DictionaryIterator {
DictionaryIterator::new(self)
}
pub fn as_ptr(&self) -> *const AVDictionary {
self.ptr.as_ptr()
}
pub fn as_mut_ptr_ref(&mut self) -> &mut *mut AVDictionary {
self.ptr.as_mut()
}
pub fn into_ptr(self) -> *mut AVDictionary {
self.ptr.into_inner()
}
}
pub struct DictionaryIterator<'a> {
dict: &'a Dictionary,
entry: *mut AVDictionaryEntry,
}
impl<'a> DictionaryIterator<'a> {
pub fn new(dict: &'a Dictionary) -> Self {
Self {
dict,
entry: std::ptr::null_mut(),
}
}
}
impl<'a> Iterator for DictionaryIterator<'a> {
type Item = (&'a CStr, &'a CStr);
fn next(&mut self) -> Option<Self::Item> {
self.entry = unsafe { av_dict_get(self.dict.as_ptr(), &[0] as *const _ as _, self.entry, AV_DICT_IGNORE_SUFFIX) };
if self.entry.is_null() {
None
} else {
let key = unsafe { CStr::from_ptr((*self.entry).key) };
let value = unsafe { CStr::from_ptr((*self.entry).value) };
Some((key, value))
}
}
}
impl<'a> IntoIterator for &'a Dictionary {
type IntoIter = DictionaryIterator<'a>;
type Item = <DictionaryIterator<'a> as Iterator>::Item;
fn into_iter(self) -> Self::IntoIter {
DictionaryIterator::new(self)
}
}
impl From<HashMap<String, String>> for Dictionary {
fn from(map: HashMap<String, String>) -> Self {
let mut dict = Dictionary::new();
for (key, value) in map {
if key.is_empty() || value.is_empty() {
continue;
}
dict.set(&key, &value).expect("Failed to set dictionary entry");
}
dict
}
}
impl From<&Dictionary> for HashMap<String, String> {
fn from(dict: &Dictionary) -> Self {
dict.into_iter()
.map(|(key, value)| (key.to_string_lossy().into_owned(), value.to_string_lossy().into_owned()))
.collect()
}
}