paper-gap 0.1.2

Local CLI for finding research papers with missing, weak, or stale public code
Documentation
use paper_gap::{analyse, arxiv::Paper, report};

fn result(title: &str, score: u32) -> report::PaperResult {
    report::PaperResult {
        paper: Paper {
            id: title.into(),
            title: title.into(),
            abstract_text: "abstract".into(),
            authors: vec!["Ada".into()],
            published_date: Some("2026-01-01".into()),
            updated_date: None,
            doi: None,
            arxiv_id: Some("2601.00001".into()),
            url: "https://arxiv.org/abs/2601.00001".into(),
            categories: vec!["cs.PL".into()],
            extracted_methods: Vec::new(),
            source_provider: "arxiv".into(),
        },
        code_links: Vec::new(),
        repositories: Vec::new(),
        gap_assessment: analyse::GapAssessment {
            implementation_gap_score: score,
            components: analyse::ScoreComponents {
                no_code_score: score,
                ..Default::default()
            },
            gaps: vec![analyse::Gap {
                gap_type: analyse::GapType::NoPublicImplementationFound,
                points: score,
                explanation: "because".into(),
            }],
        },
    }
}

#[test]
fn markdown_ranks_by_gap_score_and_explains_components() {
    let scan = report::ScanReport {
        paper_results: vec![result("low", 1), result("high", 50)],
    };
    let markdown = report::render(&scan, report::OutputFormat::Markdown).unwrap();
    assert!(markdown.find("## 1. high").unwrap() < markdown.find("## 2. low").unwrap());
    assert!(markdown.contains("Implementation gap score: 50"));
    assert!(markdown.contains("no_code_score: 50"));
    assert!(markdown.contains("NoPublicImplementationFound: +50 — because"));
}

#[test]
fn json_round_trips_scan_report() {
    let scan = report::ScanReport {
        paper_results: vec![result("paper", 50)],
    };
    let json = report::render(&scan, report::OutputFormat::Json).unwrap();
    let parsed: report::ScanReport = serde_json::from_str(&json).unwrap();
    assert_eq!(parsed.paper_results.len(), 1);
    assert_eq!(
        parsed.paper_results[0]
            .gap_assessment
            .implementation_gap_score,
        50
    );
}