Skip to main content

impactsense_parser/
project.rs

1//! High-level project parsing API for library and MCP consumers.
2
3use std::path::Path;
4
5use thiserror::Error;
6
7use crate::extract::{parse_files_to_ir, scan_and_build_ir, ExtractOptions};
8use crate::pipeline::ScanOptions;
9use crate::store::{GraphStore, InMemoryGraph, RefreshReport};
10
11#[derive(Debug, Error)]
12pub enum ProjectError {
13    #[error("scan/extract failed: {0}")]
14    Extract(#[from] crate::extract::ExtractError),
15}
16
17/// Parse an entire workspace and return a queryable in-memory graph.
18pub fn parse_project(root: &Path, scan: &ScanOptions) -> Result<InMemoryGraph, ProjectError> {
19    let extract_opts = ExtractOptions::from(&scan.graph);
20    let ir = scan_and_build_ir(root, &extract_opts, scan)?;
21    Ok(InMemoryGraph::from_ir(ir))
22}
23
24/// Incrementally refresh changed files in an existing graph.
25pub fn refresh_files(
26    graph: &mut InMemoryGraph,
27    root: &Path,
28    cleanup_targets: &[String],
29    parse_targets: &[String],
30    scan: &ScanOptions,
31) -> Result<RefreshReport, ProjectError> {
32    let extract_opts = ExtractOptions::from(&scan.graph);
33
34    for path in cleanup_targets {
35        graph.remove_file(path);
36    }
37
38    let nodes_before = graph.node_count();
39    let edges_before = graph.edge_count();
40
41    if !parse_targets.is_empty() {
42        let delta = parse_files_to_ir(root, parse_targets, &extract_opts, scan)?;
43        graph.merge_ir(delta);
44    }
45
46    Ok(RefreshReport {
47        cleanup_targets: cleanup_targets.len(),
48        parse_targets: parse_targets.len(),
49        nodes_merged: graph.node_count().saturating_sub(nodes_before),
50        edges_merged: graph.edge_count().saturating_sub(edges_before),
51    })
52}