mock_gen 0.1.23

Interface and mock generator for C++ classes
#![allow(unused)]

use crate::args::{ApiMockCmd, Commands, InterfaceCmd, MockCmd, MockGen};
use crate::prelude::*;
use crate::utils::gen_interface::*;
use crate::utils::gen_mock::*;
use crate::utils::parse_func::*;
mod args;
mod error;
mod prelude;
mod utils;

use clap::{Args, Command, Parser};
use walkdir::{DirEntry, WalkDir};

fn generate_interface(cmds: InterfaceCmd) -> Result<()> {
    let class_string = find_class_substring(cmds.file.as_str(), cmds.class.as_str()).unwrap();
    let functions = find_function_names(&class_string).unwrap();
    let template_option = find_class_template(cmds.file.as_str(), cmds.class.as_str());
    let opt_str: Option<&str> = template_option.as_deref();
    replace_line(functions, &cmds.file, &cmds.class, opt_str).unwrap();
    Ok(())
}

fn generate_mock(cmds: MockCmd) -> Result<()> {
    let class_string = find_class_substring(cmds.file.as_str(), cmds.class.as_str()).unwrap();
    let mut functions = find_function_names(&class_string).unwrap();

    let output = match cmds.output {
        Some(x) => x,
        _ => ".".to_string(),
    };

    let interface_name = get_interface_name(cmds.class.as_str());

    generate_mock_file(
        &output,
        cmds.file.as_str(),
        cmds.class.as_str(),
        interface_name.as_str(),
        &mut functions,
    )?;
    Ok(())
}

fn generate_cmock(cmds: ApiMockCmd) -> Result<()> {
    //find all class strings
    let mut functions = find_c_function_names(cmds.file.as_str()).unwrap();

    //if output specified use it or just go to working dir
    let output_mock = match cmds.output {
        Some(x) => x,
        _ => "mocks".to_string(),
    };

    let output_api = match cmds.output_api {
        Some(x) => x,
        _ => "api_headers".to_string(),
    };
    generate_cmock_file(&output_mock, &output_api, &cmds.file, &mut functions)?;
    Ok(())
}

fn find_file_path(file_name: &str) -> Result<String> {
    let path = std::env::current_dir().unwrap();
    dbg!(&path);

    if cfg!(windows) {
        let walker = WalkDir::new(path).into_iter();
        for entry in walker {
            let entry = entry.unwrap();
            let string_path = entry.path().to_str().unwrap();
            if string_path.contains(file_name) {
                let file_result: Result<&str> = match string_path.rsplit_once("\\") {
                    Some(val) => Ok(val.1),
                    _ => continue,
                };
                let file_path = file_result.unwrap();

                match file_path {
                    _ if file_path == file_name => return Ok(string_path.to_owned()),
                    _ => continue,
                }
            }
        }
        Err(Error::Generic(
            format!("error: {file_name} not found").to_owned(),
        ))
    } else {
        for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
            if entry.file_name().to_str().unwrap() == file_name {
                return Ok(entry.path().display().to_string());
            }
        }
        Err(Error::Generic(
            format!("error: {file_name} not found").to_owned(),
        ))
    }
}

fn main() -> Result<()> {
    let mut args = MockGen::parse();

    match args.commands {
        Commands::Interface(mut command) => {
            println!(
                "generating interface for class:{} in file: {}",
                command.class, command.file
            );
            command.file = find_file_path(&command.file)?;
            generate_interface(command);
        }
        Commands::Mock(mut command) => {
            println!(
                "generating mock of class:{} from file:{}",
                command.class, command.file
            );
            command.file = find_file_path(&command.file)?;
            generate_mock(command);
        }
        Commands::ApiMock(mut command) => {
            println!("generating c mock of from file:{}", command.file);
            command.file = find_file_path(&command.file)?;
            generate_cmock(command);
        }
        Commands::IMock(mut command) => {
            println!(
                "generating interface and mock of {} from file:{}",
                command.class, command.file
            );
            command.file = find_file_path(&command.file)?;

            let interface_cmd = InterfaceCmd {
                file: command.file.clone(),
                class: command.class.clone(),
            };

            let mock_cmd = MockCmd {
                file: command.file.clone(),
                class: command.class.clone(),
                output: command.output.clone(),
            };

            generate_interface(interface_cmd);
            generate_mock(mock_cmd);
        }
        _ => println!("{}", String::from("no arguments")),
    };
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::utils::gen_mock::*;
    use std::fs;
    #[test]
    fn test_mock_gen() {
        dbg!(generate_mock_string(
            "uint16& set_calibration_status(bool value, float32 test, int16_t why)"
        ));
    }

    #[test]
    fn testing_stm32_files() {
        let paths = fs::read_dir("./test_files/c_files").unwrap();
        for path in paths {
            let cmd = ApiMockCmd {
                file: path.as_ref().unwrap().path().to_str().unwrap().to_string(),
                output: None,
                output_api: None,
            };
            println!("Name: {}", path.unwrap().path().display());
            generate_cmock(cmd);
        }
    }

    #[test]
    fn testing_cpp_files() {
        let paths = fs::read_dir("./test_files/c_files").unwrap();
        todo!("add in tests for regular mocking of classes");
    }

    use crate::utils::lizard::*;

    use serde_json::Value;
    use std::env;
    use std::path::Path;
    use std::process::Command;

    #[test]
    fn test_lizard_file() {
        match env::current_dir() {
            Ok(path) => println!("Current directory: {}", path.display()),
            Err(e) => eprintln!("Error getting current directory: {}", e),
        }
        // The file containing the function
        let file_path = "./test_files/lizard/test.cpp";
        // The function we want to analyze
        let function_name = "max_of_two";

        // Run lizard with JSON output
        let output = Command::new("lizard")
            .args(["--csv", file_path])
            .output()
            .expect("Failed to run lizard");

        let output_str = String::from_utf8_lossy(&output.stdout);
        let output_str = output_str.replace(r#"\""#, r#"""#);
        println!("{}", output_str);
        let lizard_metrics = parse_func::parse_lizard_csv(&output_str, function_name).unwrap();
        println!(
            "Function: {}, NLOC: {}, CCN: {}",
            lizard_metrics.function_name, lizard_metrics.nloc, lizard_metrics.cyclomatic_complexity
        );
    }
}