trixy 0.4.0

A rust crate used to generate multi-language apis for your application
Documentation
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use syn::{punctuated::Punctuated, Token};

use crate::{
    command_enum_parsing::{Field, FunctionDeclaration, NamespacePath},
    DataCommandEnum,
};

pub fn generate_add_lua_functions_to_globals(input: &DataCommandEnum) -> TokenStream2 {
    fn turn_field_to_functions(
        input: &Punctuated<Field, Token![,]>,
        namespace_path: Option<&NamespacePath>,
    ) -> TokenStream2 {
        input
            .iter()
            .map(|field| match field {
                Field::Function(function) => generate_function_adder(function, namespace_path),
                Field::Namespace(namespace) => {
                    let mut passed_namespace =
                        namespace_path.unwrap_or(&Default::default()).clone();
                    namespace
                        .path
                        .clone()
                        .into_iter()
                        .for_each(|val| passed_namespace.push(val));

                    turn_field_to_functions(&namespace.fields, Some(&passed_namespace))
                }
            })
            .collect()
    }
    let function_adders: TokenStream2 = turn_field_to_functions(&input.fields, None);

    quote! {
        pub fn add_lua_functions_to_globals(
            lua: mlua::Lua,
            tx: tokio::sync::mpsc::Sender<Event>,
        ) -> mlua::Lua {
            lua.set_app_data(tx);
            let globals = lua.globals();

            #function_adders

            drop(globals);
            lua
        }
    }
}

fn generate_function_adder(
    field: &FunctionDeclaration,
    namespace_path: Option<&NamespacePath>,
) -> TokenStream2 {
    let field_ident = &field.name;

    let function_ident = format_ident!("wrapped_lua_function_{}", field_ident);
    let function_name = field_ident.to_string();

    let setter = if let Some(namespace_path) = namespace_path {
        // ```lua
        // local globals = {
        //     ns1: {
        //         ns_value,
        //         ns_value2,
        //     },
        //     ns2: {
        //         ns_value3,
        //     }
        // }
        // ns1.ns_value
        // ```
        let mut counter = 0;
        let namespace_table_gen: TokenStream2 = namespace_path.iter().map(|path| {
            let path = path.to_string();
            counter += 1;
            let mut set_function: TokenStream2 = Default::default();
            if counter == namespace_path.len() {
                set_function = quote! {
                    table.set(#function_name, #function_ident).expect(
                        "Setting a static global value should work"
                    );
                };
            }
            quote! {
                let table: mlua::Table = {
                    if table.contains_key(#path).expect("This check should work") {
                        let table2 = table.get(#path).expect("This was already checked");
                        table2
                    } else {
                        table.set(#path, lua.create_table().expect("This should also always work")).expect("Setting this value should work");
                        table.get(#path).expect("This was set, just above")
                    }
                };
                #set_function
            }
        }).collect();

        quote! {
            let table = &globals;
            {
                #namespace_table_gen
            }
        }
    } else {
        quote! {
            globals.set(#function_name, #function_ident).expect(
                "Setting a static global value should work"
            );
        }
    };
    quote! {
        {
            let #function_ident = lua.create_async_function(#field_ident).expect(
                &format!(
                    "The function: `{}` should be defined",
                    #function_name
                )
            );
            #setter
        }
    }
}