leankg 0.16.7

Lightweight Knowledge Graph for AI-Assisted Development
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashSet;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityMetrics {
    pub precision: f32,
    pub recall: f32,
    pub f1_score: f32,
    pub correct_files: Vec<String>,
    pub incorrect_files: Vec<String>,
    pub missing_files: Vec<String>,
}

impl QualityMetrics {
    pub fn calculate(expected: &[String], actual: &[String]) -> Self {
        let expected_set: HashSet<_> = expected.iter().collect();
        let actual_set: HashSet<_> = actual.iter().collect();

        let correct: Vec<_> = expected_set.intersection(&actual_set).collect();
        let incorrect: Vec<_> = actual_set.difference(&expected_set).collect();
        let missing: Vec<_> = expected_set.difference(&actual_set).collect();

        let correct_count = correct.len() as f32;
        let incorrect_count = incorrect.len() as f32;
        let missing_count = missing.len() as f32;

        let precision = if correct_count + incorrect_count > 0.0 {
            correct_count / (correct_count + incorrect_count)
        } else {
            0.0
        };

        let recall = if correct_count + missing_count > 0.0 {
            correct_count / (correct_count + missing_count)
        } else {
            0.0
        };

        let f1_score = if precision + recall > 0.0 {
            2.0 * (precision * recall) / (precision + recall)
        } else {
            0.0
        };

        QualityMetrics {
            precision,
            recall,
            f1_score,
            correct_files: correct.iter().map(|s| (*s).to_string()).collect(),
            incorrect_files: incorrect.iter().map(|s| (*s).to_string()).collect(),
            missing_files: missing.iter().map(|s| (*s).to_string()).collect(),
        }
    }

    pub fn verdict(&self) -> &str {
        match self.f1_score {
            0.9..=1.0 => "EXCELLENT",
            0.7..=0.9 => "GOOD",
            0.5..=0.7 => "MODERATE",
            _ => "POOR",
        }
    }
}

pub struct ContextParser;

impl ContextParser {
    pub fn parse_file_paths(stdout: &str) -> Vec<String> {
        let mut files = Vec::new();
        let patterns = [
            r"src/[\w\./-]+",
            r"lib/[\w\./-]+",
            r"tests/[\w\./-]+",
            r"bin/[\w\./-]+",
            r"cmd/[\w\./-]+",
            r"pkg/[\w\./-]+",
        ];

        for pattern in &patterns {
            if let Ok(re) = regex::Regex::new(pattern) {
                for cap in re.find_iter(stdout) {
                    let path = cap.as_str().to_string();
                    if !files.contains(&path) && Self::looks_like_valid_path(&path) {
                        files.push(path);
                    }
                }
            }
        }
        files
    }

    fn looks_like_valid_path(path: &str) -> bool {
        if path.len() < 4 {
            return false;
        }
        if path.contains('\n') || path.contains('\r') || path.contains('\t') {
            return false;
        }
        if path.starts_with('\"')
            || path.starts_with('\'')
            || path.ends_with('\"')
            || path.ends_with('\'')
        {
            return false;
        }
        if path.contains("\\n") || path.contains("\\r") || path.contains("\\t") {
            return false;
        }
        let ext = path.split('.').next_back().unwrap_or("");
        matches!(
            ext,
            "rs" | "go"
                | "py"
                | "ts"
                | "js"
                | "tsx"
                | "jsx"
                | "java"
                | "c"
                | "cpp"
                | "h"
                | "hpp"
                | "cs"
                | "rb"
                | "swift"
                | "kt"
                | "scala"
                | "yaml"
                | "yml"
                | "toml"
                | "json"
                | "xml"
                | "md"
                | "txt"
                | "sh"
                | "bash"
                | "zsh"
                | "sql"
                | "html"
                | "css"
                | "scss"
        )
    }
}