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_mut()
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 updatable: &'a mut #updatable_type2
54 }
55
56 impl <'a> libobs_wrapper::data::ObsObjectUpdater<'a> for #updater_name<'a> {
57 type ToUpdate = #updatable_type;
58
59 fn create_update(updatable: &'a mut Self::ToUpdate) -> Self {
60 Self {
61 #(#struct_initializers,)*
62 settings: libobs_wrapper::data::ObsData::new(),
63 updatable,
64 }
65 }
66
67 fn get_settings(&self) -> &libobs_wrapper::data::ObsData {
68 &self.settings
69 }
70
71 fn get_settings_mut(&mut self) -> &mut libobs_wrapper::data::ObsData {
72 &mut self.settings
73 }
74
75 fn get_id() -> libobs_wrapper::utils::ObsString {
76 #id_value.into()
77 }
78
79 fn update(self) {
80 use libobs_wrapper::utils::traits::ObsUpdatable;
81 let settings = self.settings;
82 self.updatable.update_raw(settings);
83 }
84 }
85
86 impl <'a> #updater_name <'a> {
87 #(#functions)*
88 }
89 };
90
91 TokenStream::from(expanded)
92}
93
94#[proc_macro_attribute]
95pub fn obs_object_builder(attr: TokenStream, item: TokenStream) -> TokenStream {
157 let id = parse_macro_input!(attr as LitStr);
158
159 let input = parse_macro_input!(item as DeriveInput);
160
161 let i_ident = input.ident;
162 let builder_name = format_ident!("{}", i_ident);
163
164 let generics = input.generics;
165 let visibility = input.vis;
166 let attributes = input.attrs;
167
168 let fields = match input.data {
169 Data::Struct(data) => match data.fields {
170 Fields::Named(fields) => fields.named,
171 _ => panic!("Only named fields are supported"),
172 },
173 _ => panic!("Only structs are supported"),
174 };
175
176 let id_value = id.value();
177 let (struct_fields, struct_initializers) = fields::generate_struct_fields(&fields);
178
179 let functions = obs_properties_to_functions(
180 &fields,
181 quote! {
182 use libobs_wrapper::data::ObsObjectBuilder;
183 self.get_or_create_settings()
184 },
185 );
186
187 let expanded = quote! {
188 #(#attributes)*
189 #[allow(dead_code)]
190 #visibility struct #builder_name #generics {
191 #(#struct_fields,)*
192 settings: Option<libobs_wrapper::data::ObsData>,
193 hotkeys: Option<libobs_wrapper::data::ObsData>,
194 name: libobs_wrapper::utils::ObsString
195 }
196
197 impl libobs_wrapper::data::ObsObjectBuilder for #builder_name {
198 fn new(name: impl Into<libobs_wrapper::utils::ObsString>) -> Self {
199 Self {
200 #(#struct_initializers,)*
201 name: name.into(),
202 hotkeys: None,
203 settings: None
204 }
205 }
206
207 fn get_settings(&self) -> &Option<libobs_wrapper::data::ObsData> {
208 &self.settings
209 }
210
211 fn get_settings_mut(&mut self) -> &mut Option<libobs_wrapper::data::ObsData> {
212 &mut self.settings
213 }
214
215 fn get_hotkeys(&self) -> &Option<libobs_wrapper::data::ObsData> {
216 &self.hotkeys
217 }
218
219 fn get_hotkeys_mut(&mut self) -> &mut Option<libobs_wrapper::data::ObsData> {
220 &mut self.hotkeys
221 }
222
223 fn get_name(&self) -> libobs_wrapper::utils::ObsString {
224 self.name.clone()
225 }
226
227 fn get_id() -> libobs_wrapper::utils::ObsString {
228 #id_value.into()
229 }
230 }
231
232 impl #builder_name {
233 #(#functions)*
234 }
235 };
236
237 TokenStream::from(expanded)
238}
239
240#[proc_macro_attribute]
241pub fn obs_object_impl(_attr: TokenStream, item: TokenStream) -> TokenStream {
242 let input = parse_macro_input!(item as ItemImpl);
243
244 let impl_item = input.items;
246 let impl_item2 = impl_item.clone();
247
248 let base_name = if let Type::Path(TypePath { path, .. }) = &*input.self_ty {
250 path.segments.last().unwrap().ident.to_string()
251 } else {
252 panic!("Only path types are supported in self_ty")
253 };
254
255 let builder_name = format_ident!("{}Builder", base_name);
256 let updater_name = format_ident!("{}Updater", base_name);
257
258 let expanded = quote! {
259 impl #builder_name {
261 #(#impl_item)*
262 }
263
264 impl<'a> #updater_name<'a> {
266 #(#impl_item2)*
267 }
268 };
269
270 TokenStream::from(expanded)
271}