libobs_wrapper\data/
updater.rs

1use crate::{
2    run_with_obs,
3    runtime::ObsRuntime,
4    unsafe_send::SmartPointerSendable,
5    utils::{ObsError, ObsString},
6};
7
8#[derive(Debug)]
9pub enum ObsDataChange {
10    String(ObsString, ObsString),
11    Int(ObsString, i64),
12    Bool(ObsString, bool),
13    Double(ObsString, f64),
14}
15
16#[derive(Debug)]
17/// This struct is used to update the ObsData in bulk, rather than having to call the set_string
18/// methods all the time.
19/// This reduces the load to the runtime, as only one closure has to run on the OBS runtime once,
20/// rather than multiple operations requiring multiple closures to be run on the OBs runtime.
21/// Important: Make sure to call `apply()` after setting the values.
22///
23/// This will apply the changes to the `ObsData` object.
24#[must_use = "The `apply()` method must be called to apply changes."]
25pub struct ObsDataUpdater {
26    changes: Vec<ObsDataChange>,
27    runtime: ObsRuntime,
28    data_ptr: SmartPointerSendable<*mut libobs::obs_data_t>,
29}
30
31impl ObsDataUpdater {
32    pub(super) fn new(
33        data_ptr: SmartPointerSendable<*mut libobs::obs_data_t>,
34        runtime: ObsRuntime,
35    ) -> Self {
36        ObsDataUpdater {
37            changes: Vec::new(),
38            data_ptr,
39            runtime,
40        }
41    }
42
43    pub fn set_string_ref(&mut self, key: impl Into<ObsString>, value: impl Into<ObsString>) {
44        let key = key.into();
45        let value = value.into();
46
47        log::trace!("Setting string: {:?} = {:?}", key, value);
48        self.changes.push(ObsDataChange::String(key, value));
49    }
50
51    pub fn set_string(mut self, key: impl Into<ObsString>, value: impl Into<ObsString>) -> Self {
52        self.set_string_ref(key, value);
53        self
54    }
55
56    pub fn set_int_ref(&mut self, key: impl Into<ObsString>, value: i64) {
57        let key = key.into();
58        self.changes.push(ObsDataChange::Int(key, value));
59    }
60
61    pub fn set_int(mut self, key: impl Into<ObsString>, value: i64) -> Self {
62        self.set_int_ref(key, value);
63        self
64    }
65
66    pub fn set_bool_ref(&mut self, key: impl Into<ObsString>, value: bool) {
67        let key = key.into();
68        self.changes.push(ObsDataChange::Bool(key, value));
69    }
70
71    pub fn set_bool(mut self, key: impl Into<ObsString>, value: bool) -> Self {
72        self.set_bool_ref(key, value);
73        self
74    }
75
76    pub fn apply(self) -> Result<(), ObsError> {
77        let ObsDataUpdater {
78            changes,
79            data_ptr,
80            runtime,
81        } = self;
82
83        let data_ptr = data_ptr.clone();
84        run_with_obs!(runtime, (data_ptr), move || unsafe {
85            // Safety: All pointers are held within the changes type and data_ptr is valid because we are using a SmartPointer.
86
87            for change in changes {
88                match change {
89                    ObsDataChange::String(key, value) => libobs::obs_data_set_string(
90                        data_ptr.get_ptr(),
91                        key.as_ptr().0,
92                        value.as_ptr().0,
93                    ),
94                    ObsDataChange::Int(key, value) => {
95                        libobs::obs_data_set_int(data_ptr.get_ptr(), key.as_ptr().0, value)
96                    }
97                    ObsDataChange::Bool(key, value) => {
98                        libobs::obs_data_set_bool(data_ptr.get_ptr(), key.as_ptr().0, value)
99                    }
100                    ObsDataChange::Double(key, value) => {
101                        libobs::obs_data_set_double(data_ptr.get_ptr(), key.as_ptr().0, value)
102                    }
103                };
104            }
105        })
106    }
107
108    #[deprecated = "Use `apply()` instead."]
109    pub fn update(self) -> Result<(), ObsError> {
110        self.apply()
111    }
112}