libobs_source_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]
13pub fn obs_object_updater(attr: TokenStream, item: TokenStream) -> TokenStream {
17 let u_input = parse_macro_input!(attr as UpdaterInput);
18 let id_value = u_input.name.value();
19 let updatable_type = u_input.updatable_type;
20
21 let input = parse_macro_input!(item as DeriveInput);
22
23 let i_ident = input.ident;
24 let updater_name = format_ident!("{}", i_ident);
25
26 let visibility = input.vis;
27 let attributes = input.attrs;
28
29 let fields = match input.data {
30 Data::Struct(data) => match data.fields {
31 Fields::Named(fields) => fields.named,
32 _ => panic!("Only named fields are supported"),
33 },
34 _ => panic!("Only structs are supported"),
35 };
36
37 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
38 let functions = obs_properties_to_functions(
39 &fields,
40 quote! {
41 use libobs_wrapper::data::ObsObjectUpdater;
42 self.get_settings_updater()
43 },
44 );
45
46 let updatable_type2 = updatable_type.clone();
47 let expanded = quote! {
48 #(#attributes)*
49 #[allow(dead_code)]
50 #visibility struct #updater_name<'a> {
51 #(#struct_fields,)*
52 settings: libobs_wrapper::data::ObsData,
53 settings_updater: libobs_wrapper::data::ObsDataUpdater,
54 updatable: &'a mut #updatable_type2
55 }
56
57 #[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
58 impl <'a> libobs_wrapper::data::ObsObjectUpdater<'a> for #updater_name<'a> {
59 type ToUpdate = #updatable_type;
60
61 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
62 async fn create_update(runtime: libobs_wrapper::runtime::ObsRuntime, updatable: &'a mut Self::ToUpdate) -> Result<Self, libobs_wrapper::utils::ObsError> {
63 let mut settings = libobs_wrapper::data::ObsData::new(runtime.clone()).await?;
64
65 Ok(Self {
66 #(#struct_initializers,)*
67 settings_updater: settings.bulk_update(),
68 settings,
69 updatable,
70 })
71 }
72
73 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
74 &self.settings
75 }
76
77 fn get_settings_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
78 &mut self.settings_updater
79 }
80
81 fn get_id() -> libobs_wrapper::utils::ObsString {
82 #id_value.into()
83 }
84
85 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
86 async fn update(self) -> Result<(), libobs_wrapper::utils::ObsError> {
87 use libobs_wrapper::utils::traits::ObsUpdatable;
88 let #updater_name {
89 settings_updater,
90 updatable,
91 settings,
92 ..
93 } = self;
94
95 log::trace!("Updating settings for {:?}", Self::get_id());
96 settings_updater.update().await?;
97
98 log::trace!("Updating raw settings for {:?}", Self::get_id());
99 let e = updatable.update_raw(settings).await;
100 log::trace!("Update done for {:?}", Self::get_id());
101
102 e
103 }
104 }
105
106 impl <'a> #updater_name <'a> {
107 #(#functions)*
108 }
109 };
110
111 TokenStream::from(expanded)
112}
113
114#[proc_macro_attribute]
115pub fn obs_object_builder(attr: TokenStream, item: TokenStream) -> TokenStream {
177 let id = parse_macro_input!(attr as LitStr);
178
179 let input = parse_macro_input!(item as DeriveInput);
180
181 let i_ident = input.ident;
182 let builder_name = format_ident!("{}", i_ident);
183
184 let generics = input.generics;
185 let visibility = input.vis;
186 let attributes = input.attrs;
187
188 let fields = match input.data {
189 Data::Struct(data) => match data.fields {
190 Fields::Named(fields) => fields.named,
191 _ => panic!("Only named fields are supported"),
192 },
193 _ => panic!("Only structs are supported"),
194 };
195
196 let id_value = id.value();
197 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
198
199 let functions = obs_properties_to_functions(
200 &fields,
201 quote! {
202 use libobs_wrapper::data::ObsObjectBuilder;
203 self.get_settings_updater()
204 },
205 );
206
207 let expanded = quote! {
208 #(#attributes)*
209 #[allow(dead_code)]
210 #visibility struct #builder_name #generics {
211 #(#struct_fields,)*
212 settings: libobs_wrapper::data::ObsData,
213 settings_updater: libobs_wrapper::data::ObsDataUpdater,
214 hotkeys: libobs_wrapper::data::ObsData,
215 hotkeys_updater: libobs_wrapper::data::ObsDataUpdater,
216 name: libobs_wrapper::utils::ObsString,
217 runtime: libobs_wrapper::runtime::ObsRuntime
218 }
219
220 #[cfg_attr(not(feature = "blocking"), async_trait::async_trait)]
221 impl libobs_wrapper::data::ObsObjectBuilder for #builder_name {
222 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
223 async fn new<T: Into<libobs_wrapper::utils::ObsString> + Send + Sync>(name: T, runtime: libobs_wrapper::runtime::ObsRuntime) -> Result<Self, libobs_wrapper::utils::ObsError> {
224 let mut hotkeys = libobs_wrapper::data::ObsData::new(runtime.clone()).await?;
225 let mut settings = libobs_wrapper::data::ObsData::new(runtime.clone()).await?;
226
227 Ok(Self {
228 #(#struct_initializers,)*
229 name: name.into(),
230 settings_updater: settings.bulk_update(),
231 settings,
232 hotkeys_updater: hotkeys.bulk_update(),
233 hotkeys,
234 runtime
235 })
236 }
237
238 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
239 &self.settings
240 }
241
242 fn get_settings_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
243 &mut self.settings_updater
244 }
245
246 fn get_hotkeys(&self) -> &libobs_wrapper::data::ObsData {
247 &self.hotkeys
248 }
249
250 fn get_hotkeys_updater(&mut self) -> &mut libobs_wrapper::data::ObsDataUpdater {
251 &mut self.hotkeys_updater
252 }
253
254 fn get_name(&self) -> libobs_wrapper::utils::ObsString {
255 self.name.clone()
256 }
257
258 fn get_id() -> libobs_wrapper::utils::ObsString {
259 #id_value.into()
260 }
261
262 #[cfg_attr(feature = "blocking", remove_async_await::remove_async_await)]
263 async fn build(self) -> Result<libobs_wrapper::utils::ObjectInfo, libobs_wrapper::utils::ObsError> {
264 let name = self.get_name();
265 let #builder_name {
266 settings_updater,
267 hotkeys_updater,
268 settings,
269 hotkeys,
270 ..
271 } = self;
272
273 settings_updater.update().await?;
274 hotkeys_updater.update().await?;
275
276 Ok(libobs_wrapper::utils::ObjectInfo::new(
277 Self::get_id(),
278 name,
279 Some(settings),
280 Some(hotkeys),
281 ))
282 }
283 }
284
285 impl #builder_name {
286 #(#functions)*
287 }
288 };
289
290 TokenStream::from(expanded)
291}
292
293#[proc_macro_attribute]
294pub fn obs_object_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
295 let input = parse_macro_input!(item as ItemImpl);
296
297 let impl_item = input.items;
299 let impl_item2 = impl_item.clone();
300
301 let base_name = if let Type::Path(TypePath { path, .. }) = &*input.self_ty {
303 path.segments.last().unwrap().ident.to_string()
304 } else {
305 panic!("Only path types are supported in self_ty")
306 };
307
308 let builder_name = format_ident!("{}Builder", base_name);
309 let updater_name = format_ident!("{}Updater", base_name);
310
311 let expanded = quote! {
312 impl #builder_name {
314 #(#impl_item)*
315 }
316
317 impl<'a> #updater_name<'a> {
319 #(#impl_item2)*
320 }
321 };
322
323 TokenStream::from(expanded)
324}