libobs_source_macro/
obs_properties.rs

1use 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}