mock_gen 0.1.23

Interface and mock generator for C++ classes
//! module for parsing out the functions
#![crate_name = "doc"]

use regex::Regex;
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use std::io::{BufRead, BufReader};

use crate::prelude::*;

pub fn find_class_substring(filename: &str, class_name: &str) -> Result<String> {
    let mut owned_string = "".to_owned();
    let mut lines = BufReader::new(OpenOptions::new().read(true).open(filename).unwrap())
        .lines()
        .map(|line| line.unwrap())
        .collect::<Vec<String>>();
    let mut bracket_count = 0;
    let mut offset = 0;
    let class_string = "class ".to_owned() + class_name;

    for i in 0..lines.len() {
        if lines[i].contains(&class_string) {
            if lines[i].contains("{") {
                bracket_count += 1;
            }
            loop {
                offset += 1;
                owned_string = owned_string + &lines[i + offset];
                if lines[i + offset].contains("{") {
                    bracket_count += 1
                }
                if lines[i + offset].contains("}") {
                    bracket_count -= 1
                }
                if bracket_count == 0 {
                    break;
                }
            }
            break;
        }
    }
    Ok(owned_string)
}

/// Returns Some(template_string) if found, None otherwise.
pub fn find_class_template(file_name: &str, class_name: &str) -> Option<String> {
    let file = File::open(file_name).ok()?;
    let reader = BufReader::new(file);

    // Regex to match a template line, capturing the whole "template<...>"
    let template_regex = Regex::new(r"^\s*(template\s*<[^>]*>)").ok()?;
    let class_regex = Regex::new(&format!(r"^\s*class\s+{}\b", regex::escape(class_name))).ok()?;

    let mut template_line: Option<String> = None;

    for line in reader.lines() {
        let line = line.ok()?;
        let line_trimmed = line.trim();

        // Check if line is a template declaration
        if template_line.is_none() {
            if let Some(caps) = template_regex.captures(line_trimmed) {
                // Store the full template string, including "template<...>"
                template_line = Some(caps.get(1)?.as_str().trim().to_string());
                continue;
            }
        }

        // Check if line is the class declaration
        if class_regex.is_match(line_trimmed) {
            // Return the full template string if any
            return template_line;
        }

        // Reset template_line if template not immediately followed by class
        if template_line.is_some() {
            template_line = None;
        }
    }

    None
}

pub fn filter_incorrects(name: &str) -> Result<bool> {
    let re = Regex::new(r"\w+\(\s+?\w+?\s+?\)").unwrap();
    match re.captures(name) {
        Some(x) => Ok(false),
        _ => Ok(true),
    }
}

pub fn find_function_names(class_string: &str) -> Result<Vec<String>> {
    // Create a regular expression to match function declarations
    let re = Regex::new(r"(?:static|const|constexpr)?\s*?\w+(?:\s+|\s*\*\*?\s*|\s+\*\*?\s*|\s*\*\*?|\s+\&?\s*|\s*\&?\s+)(?:const\s*
        (?:\s+|\s+\*\*?\s*|\s*\*\*?|\s+\&?\s*|\s*\&?\s+))?\w+((\n)*)\(([^.{}:=;]*)\)\s?(?:const)?(?:;|\n|\s)").unwrap();
    dbg!(&class_string);
    // Iterate over each match and add the function name to the result vector
    let mut result = Vec::new();
    for cap in re.captures_iter(&class_string) {
        let function_name = cap.get(0).unwrap().as_str().to_string();
        if filter_incorrects(&function_name).unwrap() {
            let func_n = remove_whitespace(&function_name);
            if func_n.contains("static ") {
                continue;
            }
            let func_n_trim = &func_n[0..func_n.find(";").unwrap_or(func_n.len())];
            result.push(func_n_trim.to_owned());
        }
    }
    Ok(result)
}

pub fn find_c_function_names(filename: &str) -> Result<Vec<String>> {
    // Create a regular expression to match function declarations

    let re = Regex::new(r"(?m)^\s*[^#\s]\w+\s+?\s*?\w+((\n)*)\(([^.{}:\-=;]*)\)(?:;|\n)").unwrap();
    let mut lines = BufReader::new(OpenOptions::new().read(true).open(filename).unwrap())
        .lines()
        .map(|line| line.unwrap())
        .collect::<Vec<String>>();

    let mut edited_lines = Vec::new();
    for line in lines.iter() {
        if line.contains("//") {
            let split_line: Vec<&str> = line.split("//").collect();
            let edit = split_line[0].trim().to_owned();
            edited_lines.push(edit);
        } else if line.contains("/*") {
            let split_line: Vec<&str> = line.split("/*").collect();
            let edit = split_line[0].trim().to_owned();
            edited_lines.push(edit);
        } else {
            let edit = line.to_owned();
            edited_lines.push(edit);
        }
    }
    let mut file_string: String = edited_lines.join("\n");
    // Iterate over each match and add the function name to the result vector
    let mut result = Vec::new();
    for cap in re.captures_iter(&file_string) {
        let function_name = cap.get(0).unwrap().as_str().to_string();
        let function_name_trimmed = remove_whitespace(&function_name);
        if function_name_trimmed.contains("return")
            || function_name_trimmed.contains("PACKED")
            || function_name_trimmed.contains("#define")
        {
            continue;
        }
        result.push(function_name_trimmed);
    }
    Ok(result)
}

fn remove_whitespace(s: &str) -> String {
    let words: Vec<_> = s.split_whitespace().collect();
    words.join(" ")
}