1#![allow(non_upper_case_globals)]
2use std::{
3 borrow::Cow,
4 ffi::{CStr, CString},
5 marker::PhantomData,
6};
7
8use obs_sys::{
9 obs_data_array_count, obs_data_array_item, obs_data_array_release, obs_data_array_t,
10 obs_data_clear, obs_data_create, obs_data_create_from_json, obs_data_create_from_json_file,
11 obs_data_create_from_json_file_safe, obs_data_erase, obs_data_get_json, obs_data_item_byname,
12 obs_data_item_get_array, obs_data_item_get_bool, obs_data_item_get_double,
13 obs_data_item_get_int, obs_data_item_get_obj, obs_data_item_get_string, obs_data_item_gettype,
14 obs_data_item_numtype, obs_data_item_release, obs_data_item_t, obs_data_number_type,
15 obs_data_number_type_OBS_DATA_NUM_DOUBLE, obs_data_number_type_OBS_DATA_NUM_INT,
16 obs_data_release, obs_data_set_default_bool, obs_data_set_default_double,
17 obs_data_set_default_int, obs_data_set_default_obj, obs_data_set_default_string, obs_data_t,
18 obs_data_type, obs_data_type_OBS_DATA_ARRAY, obs_data_type_OBS_DATA_BOOLEAN,
19 obs_data_type_OBS_DATA_NUMBER, obs_data_type_OBS_DATA_OBJECT, obs_data_type_OBS_DATA_STRING,
20 size_t,
21};
22
23use crate::{string::ObsString, wrapper::PtrWrapper};
24
25#[derive(Debug, Eq, PartialEq, Copy, Clone)]
26pub enum DataType {
27 String,
28 Int,
29 Double,
30 Boolean,
31 Object,
33 Array,
35}
36
37impl DataType {
38 pub fn new(typ: obs_data_type, numtyp: obs_data_number_type) -> Self {
39 match typ {
40 obs_data_type_OBS_DATA_STRING => Self::String,
41 obs_data_type_OBS_DATA_NUMBER => match numtyp {
42 obs_data_number_type_OBS_DATA_NUM_INT => Self::Int,
43 obs_data_number_type_OBS_DATA_NUM_DOUBLE => Self::Double,
44 _ => unimplemented!(),
45 },
46 obs_data_type_OBS_DATA_BOOLEAN => Self::Boolean,
47 obs_data_type_OBS_DATA_OBJECT => Self::Object,
48 obs_data_type_OBS_DATA_ARRAY => Self::Array,
49 _ => unimplemented!(),
50 }
51 }
52
53 unsafe fn from_item(item_ptr: *mut obs_data_item_t) -> Self {
54 let typ = obs_data_item_gettype(item_ptr);
55 let numtyp = obs_data_item_numtype(item_ptr);
56 Self::new(typ, numtyp)
57 }
58}
59
60pub trait FromDataItem {
61 fn typ() -> DataType;
62 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self;
66
67 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self);
71}
72
73impl FromDataItem for Cow<'_, str> {
74 fn typ() -> DataType {
75 DataType::String
76 }
77 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
78 let ptr = obs_data_item_get_string(item);
79 CStr::from_ptr(ptr).to_string_lossy()
80 }
81 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
82 let s = CString::new(val.as_ref()).unwrap();
83 obs_data_set_default_string(obj, name.as_ptr(), s.as_ptr());
84 }
85}
86
87impl FromDataItem for ObsString {
88 fn typ() -> DataType {
89 DataType::String
90 }
91 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
92 let ptr = obs_data_item_get_string(item);
93 ObsString::Dynamic(CStr::from_ptr(ptr).into())
94 }
95 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
96 obs_data_set_default_string(obj, name.as_ptr(), val.as_ptr());
97 }
98}
99
100macro_rules! impl_get_int {
101 ($($t:ty)*) => {
102 $(
103 impl FromDataItem for $t {
104 fn typ() -> DataType {
105 DataType::Int
106 }
107 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
108 obs_data_item_get_int(item) as $t
109 }
110 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
111 obs_data_set_default_int(obj, name.as_ptr(), val as i64)
112 }
113 }
114 )*
115 };
116}
117
118impl_get_int!(i64 u64 i32 u32 i16 u16 i8 u8 isize usize);
119
120impl FromDataItem for f64 {
121 fn typ() -> DataType {
122 DataType::Double
123 }
124 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
125 obs_data_item_get_double(item)
126 }
127 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
128 obs_data_set_default_double(obj, name.as_ptr(), val)
129 }
130}
131
132impl FromDataItem for f32 {
133 fn typ() -> DataType {
134 DataType::Double
135 }
136 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
137 obs_data_item_get_double(item) as f32
138 }
139 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
140 obs_data_set_default_double(obj, name.as_ptr(), val as f64)
141 }
142}
143
144impl FromDataItem for bool {
145 fn typ() -> DataType {
146 DataType::Boolean
147 }
148 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
149 obs_data_item_get_bool(item)
150 }
151 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, val: Self) {
152 obs_data_set_default_bool(obj, name.as_ptr(), val)
153 }
154}
155
156impl FromDataItem for DataObj<'_> {
157 fn typ() -> DataType {
158 DataType::Object
159 }
160 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
161 Self::from_raw(obs_data_item_get_obj(item))
162 }
163 unsafe fn set_default_unchecked(obj: *mut obs_data_t, name: ObsString, mut val: Self) {
164 obs_data_set_default_obj(obj, name.as_ptr(), val.as_ptr_mut())
165 }
166}
167
168impl FromDataItem for DataArray<'_> {
169 fn typ() -> DataType {
170 DataType::Array
171 }
172 unsafe fn from_item_unchecked(item: *mut obs_data_item_t) -> Self {
173 Self::from_raw(obs_data_item_get_array(item))
174 }
175 unsafe fn set_default_unchecked(_obj: *mut obs_data_t, _name: ObsString, _val: Self) {
176 unimplemented!("obs_data_set_default_array function doesn't exist")
177 }
178}
179
180pub struct DataObj<'parent> {
182 raw: *mut obs_data_t,
183 _parent: PhantomData<&'parent DataObj<'parent>>,
184}
185
186impl PtrWrapper for DataObj<'_> {
187 type Pointer = obs_data_t;
188
189 unsafe fn from_raw(raw: *mut Self::Pointer) -> Self {
190 Self {
191 raw,
192 _parent: PhantomData,
193 }
194 }
195
196 fn as_ptr(&self) -> *const Self::Pointer {
197 self.raw
198 }
199}
200
201impl Default for DataObj<'_> {
202 fn default() -> Self {
203 DataObj::new()
204 }
205}
206
207impl DataObj<'_> {
208 pub fn new() -> Self {
210 unsafe {
211 let raw = obs_data_create();
212 Self::from_raw(raw)
213 }
214 }
215
216 pub fn from_json(json_str: impl Into<ObsString>) -> Option<Self> {
218 let json_str = json_str.into();
219 unsafe {
220 let raw = obs_data_create_from_json(json_str.as_ptr());
221 if raw.is_null() {
222 None
223 } else {
224 Some(Self::from_raw(raw))
225 }
226 }
227 }
228
229 pub fn from_json_file(
233 json_file: impl Into<ObsString>,
234 backup_ext: impl Into<Option<ObsString>>,
235 ) -> Option<Self> {
236 let json_file = json_file.into();
237
238 unsafe {
239 let raw = if let Some(backup_ext) = backup_ext.into() {
240 obs_data_create_from_json_file_safe(json_file.as_ptr(), backup_ext.as_ptr())
241 } else {
242 obs_data_create_from_json_file(json_file.as_ptr())
243 };
244 if raw.is_null() {
245 None
246 } else {
247 Some(Self::from_raw(raw))
248 }
249 }
250 }
251
252 pub fn get<T: FromDataItem>(&self, name: impl Into<ObsString>) -> Option<T> {
254 let name = name.into();
255 let mut item_ptr = unsafe { obs_data_item_byname(self.as_ptr() as *mut _, name.as_ptr()) };
256 if item_ptr.is_null() {
257 return None;
258 }
259 unsafe {
261 obs_data_item_release(&mut item_ptr);
262 }
263 assert!(!item_ptr.is_null()); let typ = unsafe { DataType::from_item(item_ptr) };
266
267 if typ == T::typ() {
268 Some(unsafe { T::from_item_unchecked(item_ptr) })
269 } else {
270 None
271 }
272 }
273
274 pub fn set_default<T: FromDataItem>(
280 &mut self,
281 name: impl Into<ObsString>,
282 value: impl Into<T>,
283 ) {
284 unsafe { T::set_default_unchecked(self.as_ptr_mut(), name.into(), value.into()) }
285 }
286
287 pub fn get_json(&self) -> Option<String> {
289 unsafe {
290 let ptr = obs_data_get_json(self.raw);
291 if ptr.is_null() {
292 None
293 } else {
294 let slice = CStr::from_ptr(ptr);
295 Some(slice.to_string_lossy().into_owned())
296 }
297 }
298 }
299
300 pub fn clear(&mut self) {
302 unsafe {
303 obs_data_clear(self.raw);
304 }
305 }
306
307 pub fn remove(&mut self, name: impl Into<ObsString>) {
308 let name = name.into();
309 unsafe {
310 obs_data_erase(self.raw, name.as_ptr());
311 }
312 }
313}
314
315impl Drop for DataObj<'_> {
316 fn drop(&mut self) {
317 unsafe {
318 obs_data_release(self.raw);
319 }
320 }
321}
322
323pub struct DataArray<'parent> {
324 raw: *mut obs_data_array_t,
325 _parent: PhantomData<&'parent DataArray<'parent>>,
326}
327
328impl PtrWrapper for DataArray<'_> {
329 type Pointer = obs_data_array_t;
330
331 unsafe fn from_raw(raw: *mut Self::Pointer) -> Self {
332 Self {
333 raw,
334 _parent: PhantomData,
335 }
336 }
337
338 fn as_ptr(&self) -> *const Self::Pointer {
339 self.raw
340 }
341}
342
343impl DataArray<'_> {
344 pub fn len(&self) -> usize {
345 unsafe { obs_data_array_count(self.raw) as usize }
346 }
347
348 pub fn is_empty(&self) -> bool {
349 self.len() == 0
350 }
351
352 pub fn get(&self, index: usize) -> Option<DataObj> {
353 let ptr = unsafe { obs_data_array_item(self.raw, index as size_t) };
354 if ptr.is_null() {
355 None
356 } else {
357 Some(unsafe { DataObj::from_raw(ptr) })
358 }
359 }
360}
361
362impl Drop for DataArray<'_> {
363 fn drop(&mut self) {
364 unsafe {
365 obs_data_array_release(self.raw);
366 }
367 }
368}