libobs_simple_macro/
lib.rs1use obs_properties::obs_properties_to_functions;
2use parse::UpdaterInput;
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{parse_macro_input, Data, DeriveInput, Fields, ItemImpl, LitStr, Type, TypePath};
6
7mod docs;
8mod fields;
9mod obs_properties;
10mod parse;
11
12#[proc_macro_attribute]
32pub fn obs_object_updater(attr: TokenStream, item: TokenStream) -> TokenStream {
33 let u_input = parse_macro_input!(attr as UpdaterInput);
34 let id_value = u_input.name.value();
35 let updatable_type = u_input.updatable_type;
36
37 let input = parse_macro_input!(item as DeriveInput);
38
39 let i_ident = input.ident;
40 let updater_name = format_ident!("{}", i_ident);
41
42 let visibility = input.vis;
43 let attributes = input.attrs;
44
45 let fields = match input.data {
46 Data::Struct(data) => match data.fields {
47 Fields::Named(fields) => fields.named,
48 _ => panic!("Only named fields are supported"),
49 },
50 _ => panic!("Only structs are supported"),
51 };
52
53 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
54 let functions = obs_properties_to_functions(
55 &fields,
56 quote! {
57 use libobs_wrapper::data::ObsObjectUpdater;
58 self.get_settings_updater()
59 },
60 );
61
62 let updatable_type2 = updatable_type.clone();
63 let expanded = quote! {
64 #(#attributes)*
65 #[allow(dead_code)]
66 #visibility struct #updater_name<'a> {
67 #(#struct_fields,)*
68 settings: libobs_wrapper::data::ObsData,
69 settings_updater: libobs_wrapper::data::ObsDataUpdater,
70 updatable: &'a mut #updatable_type2
71 }
72
73 impl <'a> libobs_wrapper::data::ObsObjectUpdater<'a> for #updater_name<'a> {
74 type ToUpdate = #updatable_type;
75
76 fn create_update(runtime: libobs_wrapper::runtime::ObsRuntime, updatable: &'a mut Self::ToUpdate) -> Result<Self, libobs_wrapper::utils::ObsError> {
77 let source_id = Self::get_id();
78 let flags = unsafe {
79 libobs::obs_get_source_output_flags(source_id.as_ptr().0)
80 };
81
82 if flags == 0 {
83 return Err(libobs_wrapper::utils::ObsError::SourceNotAvailable(source_id.to_string()))
84 }
85
86 let mut settings = libobs_wrapper::data::ObsData::new(runtime.clone())?;
87
88 Ok(Self {
89 #(#struct_initializers,)*
90 settings_updater: settings.bulk_update(),
91 settings,
92 updatable,
93 })
94 }
95
96 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
97 &self.settings
98 }
99
100 fn get_settings_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
101 &mut self.settings_updater
102 }
103
104 fn get_id() -> libobs_wrapper::utils::ObsString {
105 #id_value.into()
106 }
107
108 fn update(self) -> Result<(), libobs_wrapper::utils::ObsError> {
109 use libobs_wrapper::utils::traits::ObsUpdatable;
110 let #updater_name {
111 settings_updater,
112 updatable,
113 settings,
114 ..
115 } = self;
116
117 log::trace!("Updating settings for {:?}", Self::get_id());
118 settings_updater.update()?;
119
120 log::trace!("Updating raw settings for {:?}", Self::get_id());
121 let e = updatable.update_raw(settings);
122 log::trace!("Update done for {:?}", Self::get_id());
123
124 e
125 }
126 }
127
128 impl <'a> #updater_name <'a> {
129 #(#functions)*
130 }
131 };
132
133 TokenStream::from(expanded)
134}
135
136#[proc_macro_attribute]
137pub fn obs_object_builder(attr: TokenStream, item: TokenStream) -> TokenStream {
195 let id = parse_macro_input!(attr as LitStr);
196
197 let input = parse_macro_input!(item as DeriveInput);
198
199 let i_ident = input.ident;
200 let builder_name = format_ident!("{}", i_ident);
201
202 let generics = input.generics;
203 let visibility = input.vis;
204 let attributes = input.attrs;
205
206 let fields = match input.data {
207 Data::Struct(data) => match data.fields {
208 Fields::Named(fields) => fields.named,
209 _ => panic!("Only named fields are supported"),
210 },
211 _ => panic!("Only structs are supported"),
212 };
213
214 let id_value = id.value();
215 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
216
217 let functions = obs_properties_to_functions(
218 &fields,
219 quote! {
220 use libobs_wrapper::data::ObsObjectBuilder;
221 self.get_settings_updater()
222 },
223 );
224
225 let expanded = quote! {
226 #(#attributes)*
227 #[allow(dead_code)]
228 #visibility struct #builder_name #generics {
229 #(#struct_fields,)*
230 settings: libobs_wrapper::data::ObsData,
231 settings_updater: libobs_wrapper::data::ObsDataUpdater,
232 hotkeys: libobs_wrapper::data::ObsData,
233 hotkeys_updater: libobs_wrapper::data::ObsDataUpdater,
234 name: libobs_wrapper::utils::ObsString,
235 runtime: libobs_wrapper::runtime::ObsRuntime
236 }
237
238 impl libobs_wrapper::data::ObsObjectBuilder for #builder_name {
239 fn new<T: Into<libobs_wrapper::utils::ObsString> + Send + Sync>(name: T, runtime: libobs_wrapper::runtime::ObsRuntime) -> Result<Self, libobs_wrapper::utils::ObsError> {
240 let name = name.into();
241 let source_id = Self::get_id();
242 let flags = unsafe {
243 libobs::obs_get_source_output_flags(source_id.as_ptr().0)
244 };
245
246 if flags == 0 {
247 return Err(libobs_wrapper::utils::ObsError::SourceNotAvailable(source_id.to_string()))
248 }
249
250 let mut hotkeys = libobs_wrapper::data::ObsData::new(runtime.clone())?;
251 let mut settings = libobs_wrapper::data::ObsData::new(runtime.clone())?;
252
253 Ok(Self {
254 #(#struct_initializers,)*
255 name,
256 settings_updater: settings.bulk_update(),
257 settings,
258 hotkeys_updater: hotkeys.bulk_update(),
259 hotkeys,
260 runtime
261 })
262 }
263
264 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
265 &self.settings
266 }
267
268 fn get_settings_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
269 &mut self.settings_updater
270 }
271
272 fn get_hotkeys(&self) -> &libobs_wrapper::data::ObsData {
273 &self.hotkeys
274 }
275
276 fn get_hotkeys_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
277 &mut self.hotkeys_updater
278 }
279
280 fn get_name(&self) -> libobs_wrapper::utils::ObsString {
281 self.name.clone()
282 }
283
284 fn get_id() -> libobs_wrapper::utils::ObsString {
285 #id_value.into()
286 }
287
288 fn build(self) -> Result<libobs_wrapper::utils::ObjectInfo, libobs_wrapper::utils::ObsError> {
289 let name = self.get_name();
290 let #builder_name {
291 settings_updater,
292 hotkeys_updater,
293 settings,
294 hotkeys,
295 ..
296 } = self;
297
298 settings_updater.update()?;
299 hotkeys_updater.update()?;
300
301 Ok(libobs_wrapper::utils::ObjectInfo::new(
302 Self::get_id(),
303 name,
304 Some(settings),
305 Some(hotkeys),
306 ))
307 }
308 }
309
310 impl #builder_name {
311 #(#functions)*
312 }
313 };
314
315 TokenStream::from(expanded)
316}
317
318#[proc_macro_attribute]
333pub fn obs_object_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
334 let input = parse_macro_input!(item as ItemImpl);
335
336 let impl_item = input.items;
338 let impl_item2 = impl_item.clone();
339
340 let base_name = if let Type::Path(TypePath { path, .. }) = &*input.self_ty {
342 path.segments.last().unwrap().ident.to_string()
343 } else {
344 panic!("Only path types are supported in self_ty")
345 };
346
347 let builder_name = format_ident!("{}Builder", base_name);
348 let updater_name = format_ident!("{}Updater", base_name);
349
350 let expanded = quote! {
351 impl #builder_name {
353 #(#impl_item)*
354 }
355
356 impl<'a> #updater_name<'a> {
358 #(#impl_item2)*
359 }
360 };
361
362 TokenStream::from(expanded)
363}