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 {
194 let id = parse_macro_input!(attr as LitStr);
195
196 let input = parse_macro_input!(item as DeriveInput);
197
198 let i_ident = input.ident;
199 let builder_name = format_ident!("{}", i_ident);
200
201 let generics = input.generics;
202 let visibility = input.vis;
203 let attributes = input.attrs;
204
205 let fields = match input.data {
206 Data::Struct(data) => match data.fields {
207 Fields::Named(fields) => fields.named,
208 _ => panic!("Only named fields are supported"),
209 },
210 _ => panic!("Only structs are supported"),
211 };
212
213 let id_value = id.value();
214 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
215
216 let functions = obs_properties_to_functions(
217 &fields,
218 quote! {
219 use libobs_wrapper::data::ObsObjectBuilder;
220 self.get_settings_updater()
221 },
222 );
223
224 let expanded = quote! {
225 #(#attributes)*
226 #[allow(dead_code)]
227 #visibility struct #builder_name #generics {
228 #(#struct_fields,)*
229 settings: libobs_wrapper::data::ObsData,
230 settings_updater: libobs_wrapper::data::ObsDataUpdater,
231 hotkeys: libobs_wrapper::data::ObsData,
232 hotkeys_updater: libobs_wrapper::data::ObsDataUpdater,
233 name: libobs_wrapper::utils::ObsString,
234 runtime: libobs_wrapper::runtime::ObsRuntime
235 }
236
237 impl libobs_wrapper::data::ObsObjectBuilder for #builder_name {
238 fn new<T: Into<libobs_wrapper::utils::ObsString> + Send + Sync>(name: T, runtime: libobs_wrapper::runtime::ObsRuntime) -> Result<Self, libobs_wrapper::utils::ObsError> {
239 let name = name.into();
240 let source_id = Self::get_id();
241 let flags = unsafe {
242 libobs::obs_get_source_output_flags(source_id.as_ptr().0)
243 };
244
245 if flags == 0 {
246 return Err(libobs_wrapper::utils::ObsError::SourceNotAvailable(source_id.to_string()))
247 }
248
249 let mut hotkeys = libobs_wrapper::data::ObsData::new(runtime.clone())?;
250 let mut settings = libobs_wrapper::data::ObsData::new(runtime.clone())?;
251
252 Ok(Self {
253 #(#struct_initializers,)*
254 name,
255 settings_updater: settings.bulk_update(),
256 settings,
257 hotkeys_updater: hotkeys.bulk_update(),
258 hotkeys,
259 runtime
260 })
261 }
262
263 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
264 &self.settings
265 }
266
267 fn get_settings_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
268 &mut self.settings_updater
269 }
270
271 fn get_hotkeys(&self) -> &libobs_wrapper::data::ObsData {
272 &self.hotkeys
273 }
274
275 fn get_hotkeys_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
276 &mut self.hotkeys_updater
277 }
278
279 fn get_name(&self) -> libobs_wrapper::utils::ObsString {
280 self.name.clone()
281 }
282
283 fn get_id() -> libobs_wrapper::utils::ObsString {
284 #id_value.into()
285 }
286
287 fn build(self) -> Result<libobs_wrapper::utils::ObjectInfo, libobs_wrapper::utils::ObsError> {
288 let name = self.get_name();
289 let #builder_name {
290 settings_updater,
291 hotkeys_updater,
292 settings,
293 hotkeys,
294 ..
295 } = self;
296
297 settings_updater.update()?;
298 hotkeys_updater.update()?;
299
300 Ok(libobs_wrapper::utils::ObjectInfo::new(
301 Self::get_id(),
302 name,
303 Some(settings),
304 Some(hotkeys),
305 ))
306 }
307 }
308
309 impl #builder_name {
310 #(#functions)*
311 }
312 };
313
314 TokenStream::from(expanded)
315}
316
317#[proc_macro_attribute]
332pub fn obs_object_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
333 let input = parse_macro_input!(item as ItemImpl);
334
335 let impl_item = input.items;
337 let impl_item2 = impl_item.clone();
338
339 let base_name = if let Type::Path(TypePath { path, .. }) = &*input.self_ty {
341 path.segments.last().unwrap().ident.to_string()
342 } else {
343 panic!("Only path types are supported in self_ty")
344 };
345
346 let builder_name = format_ident!("{}Builder", base_name);
347 let updater_name = format_ident!("{}Updater", base_name);
348
349 let expanded = quote! {
350 impl #builder_name {
352 #(#impl_item)*
353 }
354
355 impl<'a> #updater_name<'a> {
357 #(#impl_item2)*
358 }
359 };
360
361 TokenStream::from(expanded)
362}