libobs-source-macro 5.0.1

Helper macro for libobs-sources
Documentation
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{punctuated::Punctuated, token::Comma, Field, LitStr, MetaNameValue, Token};

use crate::docs::collect_doc;

pub fn obs_properties_to_functions(
    fields: &Punctuated<Field, Comma>,
    settings_getter: TokenStream,
) -> Vec<TokenStream> {
    let obs_properties = fields
        .iter()
        .filter_map(|f| {
            let attr = f.attrs.iter().find(|e| e.path().is_ident("obs_property"));

            attr.map(|a| (f, a))
        })
        .collect::<Vec<_>>();

    let mut functions = Vec::new();
    for (field, attr) in obs_properties {
        let field_type = &field.ty;
        let field_name = field.ident.as_ref().unwrap();

        let name_values: Punctuated<MetaNameValue, Token![,]> = attr
            .parse_args_with(Punctuated::parse_terminated)
            .unwrap_or_else(|_| {
                panic!(
                    "Field {} has invalid obs_property, should be name value",
                    field_name
                )
            });

        let type_t = &name_values
            .iter()
            .find(|e| *e.path.get_ident().unwrap() == "type_t")
            .expect("type_t is required for obs_property")
            .value;

        let type_t = match type_t {
            syn::Expr::Lit(e) => match &e.lit {
                syn::Lit::Str(s) => s.value(),
                _ => panic!("type_t must be a string"),
            },
            _ => panic!("type_t must be a string"),
        };

        #[allow(unused_variables)]
        let mut obs_settings_name = field_name.to_string();
        let pot_name = &name_values
            .iter()
            .find(|e| *e.path.get_ident().unwrap() == "settings_key");

        if let Some(n) = pot_name {
            obs_settings_name = match &n.value {
                syn::Expr::Lit(e) => match &e.lit {
                    syn::Lit::Str(s) => s.value(),
                    _ => panic!("setings_key must be a string"),
                },
                _ => panic!("settings_key must be a string"),
            };
        }

        let (_docs_str, docs_attr) = collect_doc(&field.attrs);

        let obs_settings_key = LitStr::new(&obs_settings_name, Span::call_site());
        let set_field = quote::format_ident!("set_{}", field_name);
        let type_t_str = type_t.as_str();
        let to_add = match type_t_str {
            "enum" => {
                quote! {
                    #(#docs_attr)*
                    pub fn #set_field(mut self, #field_name: #field_type) -> Self {
                        use num_traits::ToPrimitive;
                        let val = #field_name.to_i32().unwrap();

                        #settings_getter
                            .set_int_ref(#obs_settings_key, val as i64);

                        self
                    }
                }
            }
            "enum_string" => {
                quote! {
                    #(#docs_attr)*
                    pub fn #set_field(mut self, #field_name: #field_type) -> Self {
                        use libobs_wrapper::data::StringEnum;

                        #settings_getter
                            .set_string_ref(#obs_settings_key, #field_name.to_str());

                        self
                    }
                }
            }
            "string" => {
                quote! {
                    #(#docs_attr)*
                    pub fn #set_field<T: Into<libobs_wrapper::utils::ObsString> + Sync + Send>(mut self, #field_name: T) -> Self {
                        #settings_getter
                            .set_string_ref(#obs_settings_key, #field_name);
                        self
                    }
                }
            }
            "bool" => {
                quote! {
                    #(#docs_attr)*
                    pub fn #set_field(mut self, #field_name: bool) -> Self {
                        #settings_getter
                            .set_bool_ref(#obs_settings_key, #field_name);
                        self
                    }
                }
            }
            "int" => {
                quote! {
                    #(#docs_attr)*
                    pub fn #set_field(mut self, #field_name: i64) -> Self {
                        #settings_getter
                            .set_int_ref(#obs_settings_key, #field_name);
                        self
                    }
                }
            }
            _ => panic!(
                "Unsupported type_t {}. Should either be `enum`, `string`, `bool` or `int`",
                type_t
            ),
        };

        functions.push(to_add);
    }

    functions
}