weavatrix-clone 0.1.3

Evidence-first Type-1/2/3 code clone engine with deterministic families and bounded near-miss verification
Documentation
use crate::args::OutputKind;
use std::{fmt::Write, time::Duration};
use weavatrix_clone::{CloneKind, CloneReport, output};

pub(crate) fn render(
    report: &CloneReport,
    output_kind: OutputKind,
    summary_only: bool,
    elapsed: Duration,
) -> Result<String, String> {
    match output_kind {
        OutputKind::Text => Ok(text(report, summary_only, elapsed)),
        OutputKind::Json => Ok(format!("{}\n", output::to_json(report))),
        OutputKind::Sarif => Ok(format!("{}\n", output::to_sarif(report))),
        OutputKind::BigCloneEval => {
            output::to_bigcloneeval(report).map_err(|error| error.to_string())
        }
    }
}

fn text(report: &CloneReport, summary_only: bool, elapsed: Duration) -> String {
    let mut output = String::new();
    if !summary_only {
        for pair in &report.pairs {
            let _ = writeln!(
                output,
                "{}:{}-{} ~ {}:{}-{} type={:?} similarity={:.1}%",
                pair.left.path,
                pair.left.span.start_line,
                pair.left.span.end_line,
                pair.right.path,
                pair.right.span.start_line,
                pair.right.span.end_line,
                pair.kind,
                pair.similarity.percent()
            );
        }
    }
    let type1 = count(report, CloneKind::Type1);
    let type2 = count(report, CloneKind::Type2);
    let type3 = count(report, CloneKind::Type3);
    let _ = writeln!(
        output,
        concat!(
            "files={} fragments={} candidates={} pairs={} families={} ",
            "type1={} type2={} type3={} elapsed_ms={:.3}"
        ),
        report.statistics.source_files,
        report.statistics.analyzed_fragments,
        report.statistics.candidate_pairs,
        report.pairs.len(),
        report.families.len(),
        type1,
        type2,
        type3,
        elapsed.as_secs_f64() * 1_000.0
    );
    output
}

fn count(report: &CloneReport, kind: CloneKind) -> usize {
    report.pairs.iter().filter(|pair| pair.kind == kind).count()
}