#![allow(non_upper_case_globals)]
use std::{
borrow::Cow,
ffi::{CStr, CString},
marker::PhantomData,
};
use obs_sys::{
obs_data_array_count, obs_data_array_item, obs_data_array_release, obs_data_array_t,
obs_data_clear, obs_data_create, obs_data_create_from_json, obs_data_create_from_json_file,
obs_data_create_from_json_file_safe, obs_data_erase, obs_data_get_json, obs_data_item_byname,
obs_data_item_get_array, obs_data_item_get_bool, obs_data_item_get_double,
obs_data_item_get_int, obs_data_item_get_obj, obs_data_item_get_string, obs_data_item_gettype,
obs_data_item_numtype, obs_data_item_release, obs_data_item_t, obs_data_number_type,
obs_data_number_type_OBS_DATA_NUM_DOUBLE, obs_data_number_type_OBS_DATA_NUM_INT,
obs_data_release, obs_data_set_default_bool, obs_data_set_default_double,
obs_data_set_default_int, obs_data_set_default_obj, obs_data_set_default_string, obs_data_t,
obs_data_type, obs_data_type_OBS_DATA_ARRAY, obs_data_type_OBS_DATA_BOOLEAN,
obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, obs_data_type_OBS_DATA_STRING,
size_t,
};
use crate::{string::ObsString, wrapper::PtrWrapper};
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum DataType {
String,
Int,
Double,
Boolean,
Object,
Array,
}
impl DataType {
pub fn new(typ: obs_data_type, numtyp: obs_data_number_type) -> Self {
match typ {
obs_data_type_OBS_DATA_STRING => Self::String,
obs_data_type_OBS_DATA_NUMBER => match numtyp {
obs_data_number_type_OBS_DATA_NUM_INT => Self::Int,
obs_data_number_type_OBS_DATA_NUM_DOUBLE => Self::Double,
_ => unimplemented!(),
},
obs_data_type_OBS_DATA_BOOLEAN => Self::Boolean,
obs_data_type_OBS_DATA_OBJECT => Self::Object,
obs_data_type_OBS_DATA_ARRAY => Self::Array,
_ => unimplemented!(),
}
}
unsafe fn from_item(item_ptr: *mut obs_data_item_t) -> Self {
let typ = obs_data_item_gettype(item_ptr);
let numtyp = obs_data_item_numtype(item_ptr);
Self::new(typ, numtyp)
}
}
pub trait FromDataItem {
fn typ() -> DataType;
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self;
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self);
}
impl FromDataItem for Cow<'_, str> {
fn typ() -> DataType {
DataType::String
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
let ptr = obs_data_item_get_string(item);
CStr::from_ptr(ptr).to_string_lossy()
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
let s = CString::new(val.as_ref()).unwrap();
obs_data_set_default_string(obj, name.as_ptr(), s.as_ptr());
}
}
impl FromDataItem for ObsString {
fn typ() -> DataType {
DataType::String
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
let ptr = obs_data_item_get_string(item);
ObsString::Dynamic(CStr::from_ptr(ptr).into())
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
obs_data_set_default_string(obj, name.as_ptr(), val.as_ptr());
}
}
macro_rules! impl_get_int {
($($t:ty)*) => {
$(
impl FromDataItem for $t {
fn typ() -> DataType {
DataType::Int
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
obs_data_item_get_int(item) as $t
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
obs_data_set_default_int(obj, name.as_ptr(), val as i64)
}
}
)*
};
}
impl_get_int!(i64 u64 i32 u32 i16 u16 i8 u8 isize usize);
impl FromDataItem for f64 {
fn typ() -> DataType {
DataType::Double
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
obs_data_item_get_double(item)
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
obs_data_set_default_double(obj, name.as_ptr(), val)
}
}
impl FromDataItem for f32 {
fn typ() -> DataType {
DataType::Double
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
obs_data_item_get_double(item) as f32
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
obs_data_set_default_double(obj, name.as_ptr(), val as f64)
}
}
impl FromDataItem for bool {
fn typ() -> DataType {
DataType::Boolean
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
obs_data_item_get_bool(item)
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
obs_data_set_default_bool(obj, name.as_ptr(), val)
}
}
impl FromDataItem for DataObj<'_> {
fn typ() -> DataType {
DataType::Object
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
Self::from_raw(obs_data_item_get_obj(item))
}
unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, mut val: Self) {
obs_data_set_default_obj(obj, name.as_ptr(), val.as_ptr_mut())
}
}
impl FromDataItem for DataArray<'_> {
fn typ() -> DataType {
DataType::Array
}
unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
Self::from_raw(obs_data_item_get_array(item))
}
unsafe fn set_default_unchecked(_obj: *mut obs_data_t, _name: ObsString, _val: Self) {
unimplemented!("obs_data_set_default_array function doesn't exist")
}
}
pub struct DataObj<'parent> {
raw: *mut obs_data_t,
_parent: PhantomData<&'parent DataObj<'parent>>,
}
impl PtrWrapper for DataObj<'_> {
type Pointer = obs_data_t;
unsafe fn from_raw(raw: *mut Self::Pointer) -> Self {
Self {
raw,
_parent: PhantomData,
}
}
fn as_ptr(&self) -> *const Self::Pointer {
self.raw
}
}
impl Default for DataObj<'_> {
fn default() -> Self {
DataObj::new()
}
}
impl DataObj<'_> {
pub fn new() -> Self {
unsafe {
let raw = obs_data_create();
Self::from_raw(raw)
}
}
pub fn from_json(json_str: impl Into<ObsString>) -> Option<Self> {
let json_str = json_str.into();
unsafe {
let raw = obs_data_create_from_json(json_str.as_ptr());
if raw.is_null() {
None
} else {
Some(Self::from_raw(raw))
}
}
}
pub fn from_json_file(
json_file: impl Into<ObsString>,
backup_ext: impl Into<Option<ObsString>>,
) -> Option<Self> {
let json_file = json_file.into();
unsafe {
let raw = if let Some(backup_ext) = backup_ext.into() {
obs_data_create_from_json_file_safe(json_file.as_ptr(), backup_ext.as_ptr())
} else {
obs_data_create_from_json_file(json_file.as_ptr())
};
if raw.is_null() {
None
} else {
Some(Self::from_raw(raw))
}
}
}
pub fn get<T: FromDataItem>(&self, name: impl Into<ObsString>) -> Option<T> {
let name = name.into();
let mut item_ptr = unsafe { obs_data_item_byname(self.as_ptr() as *mut _, name.as_ptr()) };
if item_ptr.is_null() {
return None;
}
unsafe {
obs_data_item_release(&mut item_ptr);
}
assert!(!item_ptr.is_null());
let typ = unsafe { DataType::from_item(item_ptr) };
if typ == T::typ() {
Some(unsafe { T::from_item_unchecked(item_ptr) })
} else {
None
}
}
pub fn set_default<T: FromDataItem>(
&mut self,
name: impl Into<ObsString>,
value: impl Into<T>,
) {
unsafe { T::set_default_unchecked(self.as_ptr_mut(), name.into(), value.into()) }
}
pub fn get_json(&self) -> Option<String> {
unsafe {
let ptr = obs_data_get_json(self.raw);
if ptr.is_null() {
None
} else {
let slice = CStr::from_ptr(ptr);
Some(slice.to_string_lossy().into_owned())
}
}
}
pub fn clear(&mut self) {
unsafe {
obs_data_clear(self.raw);
}
}
pub fn remove(&mut self, name: impl Into<ObsString>) {
let name = name.into();
unsafe {
obs_data_erase(self.raw, name.as_ptr());
}
}
}
impl Drop for DataObj<'_> {
fn drop(&mut self) {
unsafe {
obs_data_release(self.raw);
}
}
}
pub struct DataArray<'parent> {
raw: *mut obs_data_array_t,
_parent: PhantomData<&'parent DataArray<'parent>>,
}
impl PtrWrapper for DataArray<'_> {
type Pointer = obs_data_array_t;
unsafe fn from_raw(raw: *mut Self::Pointer) -> Self {
Self {
raw,
_parent: PhantomData,
}
}
fn as_ptr(&self) -> *const Self::Pointer {
self.raw
}
}
impl DataArray<'_> {
pub fn len(&self) -> usize {
unsafe { obs_data_array_count(self.raw) as usize }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, index: usize) -> Option<DataObj> {
let ptr = unsafe { obs_data_array_item(self.raw, index as size_t) };
if ptr.is_null() {
None
} else {
Some(unsafe { DataObj::from_raw(ptr) })
}
}
}
impl Drop for DataArray<'_> {
fn drop(&mut self) {
unsafe {
obs_data_array_release(self.raw);
}
}
}