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