impactsense-parser 0.1.0

Multi-language static analysis: parse codebases into an in-memory dependency graph for impact analysis
Documentation
//! High-level project parsing API for library and MCP consumers.

use std::path::Path;

use thiserror::Error;

use crate::extract::{parse_files_to_ir, scan_and_build_ir, ExtractOptions};
use crate::pipeline::ScanOptions;
use crate::store::{GraphStore, InMemoryGraph, RefreshReport};

#[derive(Debug, Error)]
pub enum ProjectError {
    #[error("scan/extract failed: {0}")]
    Extract(#[from] crate::extract::ExtractError),
}

/// Parse an entire workspace and return a queryable in-memory graph.
pub fn parse_project(root: &Path, scan: &ScanOptions) -> Result<InMemoryGraph, ProjectError> {
    let extract_opts = ExtractOptions::from(&scan.graph);
    let ir = scan_and_build_ir(root, &extract_opts, scan)?;
    Ok(InMemoryGraph::from_ir(ir))
}

/// Incrementally refresh changed files in an existing graph.
pub fn refresh_files(
    graph: &mut InMemoryGraph,
    root: &Path,
    cleanup_targets: &[String],
    parse_targets: &[String],
    scan: &ScanOptions,
) -> Result<RefreshReport, ProjectError> {
    let extract_opts = ExtractOptions::from(&scan.graph);

    for path in cleanup_targets {
        graph.remove_file(path);
    }

    let nodes_before = graph.node_count();
    let edges_before = graph.edge_count();

    if !parse_targets.is_empty() {
        let delta = parse_files_to_ir(root, parse_targets, &extract_opts, scan)?;
        graph.merge_ir(delta);
    }

    Ok(RefreshReport {
        cleanup_targets: cleanup_targets.len(),
        parse_targets: parse_targets.len(),
        nodes_merged: graph.node_count().saturating_sub(nodes_before),
        edges_merged: graph.edge_count().saturating_sub(edges_before),
    })
}