libobs_source_macro/
obs_properties.rs1use proc_macro2::{Span, TokenStream};
2use quote::quote;
3use syn::{
4 punctuated::Punctuated, token::Comma, Field, LitStr, MetaNameValue, Token
5};
6
7use crate::docs::collect_doc;
8
9pub fn obs_properties_to_functions(fields: &Punctuated<Field, Comma>, settings_getter: TokenStream) -> Vec<TokenStream>{
10 let obs_properties = fields
11 .iter()
12 .filter_map(|f| {
13 let attr = f.attrs.iter().find(|e| e.path().is_ident("obs_property"));
14
15 attr.map(|a| (f, a))
16 })
17 .collect::<Vec<_>>();
18
19 let mut functions = Vec::new();
20 for (field, attr) in obs_properties {
21 let field_type = &field.ty;
22 let field_name = field.ident.as_ref().unwrap();
23
24 let name_values: Punctuated<MetaNameValue, Token![,]> = attr
25 .parse_args_with(Punctuated::parse_terminated)
26 .expect(&format!(
27 "Field {} has invalid obs_property, should be name value",
28 field_name
29 ));
30
31 let type_t = &name_values
32 .iter()
33 .find(|e| e.path.get_ident().unwrap().to_string() == "type_t")
34 .expect("type_t is required for obs_property")
35 .value;
36
37 let type_t = match type_t {
38 syn::Expr::Lit(e) => match &e.lit {
39 syn::Lit::Str(s) => s.value(),
40 _ => panic!("type_t must be a string"),
41 },
42 _ => panic!("type_t must be a string"),
43 };
44
45 #[allow(unused_variables)]
46 let mut obs_settings_name = field_name.to_string();
47 let pot_name = &name_values
48 .iter()
49 .find(|e| e.path.get_ident().unwrap().to_string() == "settings_key");
50
51 if let Some(n) = pot_name {
52 obs_settings_name = match &n.value {
53 syn::Expr::Lit(e) => match &e.lit {
54 syn::Lit::Str(s) => s.value(),
55 _ => panic!("setings_key must be a string"),
56 },
57 _ => panic!("settings_key must be a string"),
58 };
59 }
60
61 let (_docs_str, docs_attr) = collect_doc(&field.attrs);
62
63 let obs_settings_key = LitStr::new(&obs_settings_name, Span::call_site());
64 let set_field = quote::format_ident!("set_{}", field_name);
65 let type_t_str = type_t.as_str();
66 let to_add = match type_t_str {
67 "enum" => {
68 quote! {
69 #(#docs_attr)*
70 pub fn #set_field(mut self, #field_name: #field_type) -> Self {
71 use num_traits::ToPrimitive;
72 let val = #field_name.to_i32().unwrap();
73
74 #settings_getter
75 .set_int_ref(#obs_settings_key, val as i64);
76
77 self
78 }
79 }
80 }
81 "enum_string" => {
82 quote! {
83 #(#docs_attr)*
84 pub fn #set_field(mut self, #field_name: #field_type) -> Self {
85 use libobs_wrapper::data::StringEnum;
86
87 #settings_getter
88 .set_string_ref(#obs_settings_key, #field_name.to_str());
89
90 self
91 }
92 }
93 }
94 "string" => {
95 quote! {
96 #(#docs_attr)*
97 pub fn #set_field<T: Into<libobs_wrapper::utils::ObsString> + Sync + Send>(mut self, #field_name: T) -> Self {
98 #settings_getter
99 .set_string_ref(#obs_settings_key, #field_name);
100 self
101 }
102 }
103 }
104 "bool" => {
105 quote! {
106 #(#docs_attr)*
107 pub fn #set_field(mut self, #field_name: bool) -> Self {
108 #settings_getter
109 .set_bool_ref(#obs_settings_key, #field_name);
110 self
111 }
112 }
113 }
114 "int" => {
115 quote! {
116 #(#docs_attr)*
117 pub fn #set_field(mut self, #field_name: i64) -> Self {
118 #settings_getter
119 .set_int_ref(#obs_settings_key, #field_name);
120 self
121 }
122 }
123 }
124 _ => panic!(
125 "Unsupported type_t {}. Should either be `enum`, `string`, `bool` or `int`",
126 type_t
127 ),
128 };
129
130 functions.push(to_add);
131 }
132
133 functions
134}