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