funcvec-core 0.1.3

Core extraction, embedding, scoring, and reporting logic for funcvec.
Documentation
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
    Table,
    Json,
    Markdown,
}

#[derive(Debug, Clone)]
pub struct ReportConfig {
    pub provider: crate::embeddings::ProviderKind,
    pub model: Option<String>,
    pub ollama_host: String,
    pub allow_nonlocal_ollama_host: bool,
    pub ollama_timeout_secs: u64,
    pub ollama_keep_alive: Option<String>,
    pub ollama_dimensions: Option<usize>,
    pub ollama_truncate: bool,
    pub model_cache_dir: Option<PathBuf>,
    pub native_threads: Option<usize>,
    pub threshold: f32,
    pub top_k: usize,
    pub min_lines: usize,
    pub min_tokens: usize,
    pub cache_dir: Option<PathBuf>,
    pub allow_source_upload: bool,
}

impl Default for ReportConfig {
    fn default() -> Self {
        Self {
            provider: crate::embeddings::ProviderKind::Lexical,
            model: None,
            ollama_host: default_ollama_host(),
            allow_nonlocal_ollama_host: false,
            ollama_timeout_secs: 120,
            ollama_keep_alive: None,
            ollama_dimensions: None,
            ollama_truncate: true,
            model_cache_dir: None,
            native_threads: None,
            threshold: 0.72,
            top_k: 25,
            min_lines: 3,
            min_tokens: 12,
            cache_dir: None,
            allow_source_upload: false,
        }
    }
}

pub fn default_ollama_host() -> String {
    std::env::var("OLLAMA_HOST").unwrap_or_else(|_| "http://127.0.0.1:11434".to_owned())
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionRecord {
    pub id: String,
    pub name: String,
    pub file: PathBuf,
    pub start_line: usize,
    pub end_line: usize,
    pub source: String,
    pub normalized: String,
    pub token_count: usize,
    pub line_count: usize,
    pub content_hash: String,
    pub expected_group: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FunctionSummary {
    pub id: String,
    pub name: String,
    pub file: PathBuf,
    pub start_line: usize,
    pub end_line: usize,
    pub token_count: usize,
    pub line_count: usize,
    pub expected_group: Option<String>,
}

impl From<&FunctionRecord> for FunctionSummary {
    fn from(function: &FunctionRecord) -> Self {
        Self {
            id: function.id.clone(),
            name: function.name.clone(),
            file: function.file.clone(),
            start_line: function.start_line,
            end_line: function.end_line,
            token_count: function.token_count,
            line_count: function.line_count,
            expected_group: function.expected_group.clone(),
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScoreBreakdown {
    pub clone: f32,
    pub semantic: Option<f32>,
    pub hybrid: f32,
    pub clone_flag: bool,
    pub semantic_flag: bool,
    pub hybrid_flag: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Candidate {
    pub id: String,
    pub left: FunctionRecord,
    pub right: FunctionRecord,
    pub scores: ScoreBreakdown,
    pub reasons: Vec<String>,
    pub expected_match: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Report {
    pub project_root: PathBuf,
    pub provider: String,
    pub model: Option<String>,
    pub functions_count: usize,
    pub embedding_stats: EmbeddingStats,
    pub candidates: Vec<Candidate>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EmbeddingStats {
    pub cache_hits: usize,
    pub cache_misses: usize,
    pub dimensions: Option<usize>,
    pub elapsed_ms: u64,
    pub model_digest: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StrategyEval {
    pub flagged: usize,
    pub true_positives: usize,
    pub false_positives: usize,
    pub known_pairs: usize,
    pub precision: f32,
    pub recall: f32,
    pub f1: f32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalReport {
    pub report: Report,
    pub functions: Vec<FunctionSummary>,
    pub clone: StrategyEval,
    pub semantic: StrategyEval,
    pub hybrid: StrategyEval,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvalMatrixReport {
    pub project_root: PathBuf,
    pub provider: String,
    pub functions_count: usize,
    pub known_pairs: usize,
    pub threshold: f32,
    pub top_k: usize,
    pub functions: Vec<FunctionSummary>,
    pub models: Vec<ModelEvalResult>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelEvalResult {
    pub model: String,
    pub status: ModelEvalStatus,
    pub error_kind: Option<String>,
    pub error: Option<String>,
    pub report: Option<Report>,
    pub clone: Option<StrategyEval>,
    pub semantic: Option<StrategyEval>,
    pub hybrid: Option<StrategyEval>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ModelEvalStatus {
    Success,
    Failure,
}