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::{
    fs,
    path::{Path, PathBuf},
};

use crate::parser::parse_trixy_lang;
use crate::types::C_TYPE_HEADER;
use config::{
    file_tree::{FileTree, GeneratedFile},
    trixy::Language,
};

use self::config::trixy::TrixyConfig;

pub mod config;
mod generate;

const VIM_LINE_RUST: &'static str = "// vim: filetype=rust\n";
const VIM_LINE_C: &'static str = "// vim: filetype=c\n";

impl TrixyConfig {
    /// This is the heart of the trixy crate.
    /// It's main purpose is to generate, what you have asked for in the [`TrixyConfig`] and to
    /// return this generated code as a [`FileTree`].
    ///
    /// # Panic
    ///
    /// Beware that this function will panic on errors, as it is written with the expectation that
    /// it's run from a `build.rs` file. If you want to avoid that, usage of the raw generation
    /// functions is guaranteed to never panic.
    ///
    /// # Examples
    ///
    /// ```
    /// use crate::macros::TrixyConfig;
    ///
    /// let trixy_config = TrixyConfig::new("some_callback_function")
    ///                         .trixy_path("./path/to/api/file.tri")
    ///                         /* other config settings */;
    /// trixy_config.generate();
    /// ```
    #[must_use]
    pub fn generate(&self) -> FileTree {
        let mut file_tree = FileTree::new();

        let source_code = fs::read_to_string(&self.trixy_path).unwrap_or_else(|err| {
            panic! {"Can't read file at path: '{}'. The Error was: '{}'", self.trixy_path.display(), err};
        });

        let trixy_code = parse_trixy_lang(&source_code).unwrap_or_else(|err| {
            panic! {"Parsing of the trixy file failed: \n{}", err}
        });

        // host code
        if self.generate_host {
            let host_code = generate::host::generate(&trixy_code, &self);
            file_tree.add_host_file(GeneratedFile::new_in_out_dir(
                self.host_code_name.clone(),
                host_code,
                Language::Rust,
                &self.out_dir_path,
            ));
        }

        // auxiliary code
        if self.generate_auxiliary {
            let c_header = generate::auxiliary::generate(&trixy_code, &self);
            if let Some(dist_dir) = &self.dist_dir_path {
                let c_header_dist =
                    PathBuf::from(format!("{}/{}", dist_dir.display(), &self.c_header_name));
                file_tree.add_auxiliary_file(GeneratedFile::new(
                    c_header_dist,
                    c_header,
                    Language::C,
                ));

                // // TODO(@soispha): Is this even necessary? <2024-03-25>
                // let (interface_name, interface_content) = {
                //     let interface_header = format!(
                //         "\
                //     /* This file is automatcially generated by Trixy */ \n\
                //     #ifndef TRIXY_INTERFACE_H \n\
                //     #define TRIXY_INTERFACE_H \n\
                //     #include \"{}\" \n\
                //     #endif // TRIXY_INTERFACE_H \n\
                //      ",
                //         &self.c_header_name
                //     );
                //     ("interface.h", interface_header)
                // };

                if self.add_c_headers {
                    C_TYPE_HEADER
                        .iter()
                        // .chain(iter::once(&(interface_name, &interface_content[..])))
                        .for_each(|(name, content)| {
                            let path: &Path = &Path::new(name);

                            let header_path =
                                PathBuf::from(format!("{}/{}", dist_dir.display(), path.display()));
                            file_tree.add_auxiliary_file(GeneratedFile::new(
                                header_path,
                                content.to_string(),
                                Language::C,
                            ));
                        });
                }
            }
        }

        file_tree
    }
}