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 std::iter;

use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

use crate::{
    macros::config::trixy::TrixyConfig,
    parser::command_spec::{Enumeration, Identifier, Namespace, Structure, Type, Variant},
};

impl Namespace {
    pub fn to_c(&self, config: &TrixyConfig, namespaces: &Vec<&Identifier>) -> TokenStream2 {
        let mut namespaces = namespaces.clone();

        if self.name.variant != Variant::RootNamespace {
            namespaces.push(&self.name);
        } else {
            // Test to ensure, that the RootNamespace is actually at the root
            assert_eq!(namespaces.len(), 0);
        }

        let functions: TokenStream2 = self
            .functions
            .iter()
            .map(|r#fn| r#fn.to_c(&config, &namespaces))
            .collect();
        let additional_functions: TokenStream2 = self
            .namespaces
            .iter()
            .map(|nasp| nasp.to_c(&config, &namespaces))
            .collect();
        let structures: TokenStream2 = self.structures.iter().map(Structure::to_c).collect();
        let enumerations: TokenStream2 = self.enumerations.iter().map(Enumeration::to_c).collect();

        let all_results_types: TokenStream2 = self
            .select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Result")
            .iter()
            .map(|(ident, generics)| Type::to_c_result(&ident, &generics))
            .collect();
        let all_option_types: TokenStream2 = self
            .select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Option")
            .iter()
            .map(|(ident, generics)| Type::to_c_option(&ident, &generics))
            .collect();
        let all_vector_types: TokenStream2 = self
            .select_types_pred(|(identifier, _generic_args)| identifier.name.as_str() == "Vec")
            .iter()
            .map(|(ident, generics)| Type::to_c_vector(&ident, &generics))
            .collect();

        if self.name.variant == Variant::RootNamespace {
            quote! {
                #all_results_types
                #all_option_types
                #all_vector_types

                #enumerations
                #structures
                #additional_functions

                #functions
            }
        } else {
            let ident = &self.name.to_c();
            quote! {
                pub mod #ident {
                    #all_results_types
                    #all_option_types
                    #all_vector_types

                    #enumerations
                    #structures
                    #additional_functions
                }
                #functions
            }
        }
    }

    /// Get all the types used in this namespaces that conform to the predicate.
    /// The predicate get's the identifier of a type and the generic arguments passed.
    pub fn select_types_pred(
        &self,
        pred: fn(&(Identifier, Vec<Type>)) -> bool,
    ) -> Vec<(Identifier, Vec<Type>)> {
        fn filter_type(ty: &Type) -> Vec<(Identifier, Vec<Type>)> {
            match ty {
                Type::Typical {
                    identifier,
                    generic_args,
                } => {
                    let mut output = vec![];
                    output.extend(generic_args.iter().map(|ga| filter_type(ga)).flatten());
                    output.push((identifier.to_owned(), generic_args.to_owned()));
                    // Stop recursion and return
                    output
                }
                Type::Function { inputs, output } => {
                    let mut full_output = vec![];
                    full_output.extend(
                        inputs
                            .iter()
                            .map(|na| &na.r#type)
                            .map(|ty| filter_type(&ty))
                            .flatten(),
                    );

                    if let Some(out) = output {
                        full_output.extend(filter_type(out))
                    }

                    full_output
                }
            }
        }

        let all_types = self.collect_types();

        let all_result_types: Vec<_> = all_types
            .iter()
            .map(|r#type| filter_type(r#type))
            .flatten()
            .filter(pred)
            .collect();

        all_result_types
    }

    /// Collect a vector of all types used in this namespace
    pub fn collect_types(&self) -> Vec<Type> {
        let structures_types = self
            .structures
            .iter()
            .map(|st| st.contents.iter().map(|dc| dc.r#type.clone()))
            .flatten();

        self.functions
            .iter()
            .map(|r#fn| {
                r#fn.inputs
                    .iter()
                    .map(|nt| Some(nt.r#type.clone()))
                    .chain({
                        if let Some(output) = &r#fn.output {
                            iter::once(Some(output.clone()))
                        } else {
                            iter::once(None)
                        }
                    })
                    .filter_map(|x| x)
            })
            .flatten()
            .chain(structures_types)
            .collect()
    }
}