astmap-core 0.0.1

Core domain types and logic for astmap
Documentation
mod config;
mod cross_file_deps;
pub(crate) mod hasher;
mod imports;
mod indexing;
mod reconcile;
mod same_file_deps;
mod types;

use std::path::Path;

use tracing::info;

use crate::error::ScanError;
use crate::port::{FileDiscovery, LanguageRegistry, ScanStore};

pub use types::ScanResult;

/// Run a full or incremental scan.
pub fn scan(
    project_dir: &Path,
    db: &dyn ScanStore,
    full: bool,
    lang: &dyn LanguageRegistry,
    discovery: &dyn FileDiscovery,
) -> Result<ScanResult, ScanError> {
    let project_dir = project_dir
        .canonicalize()
        .unwrap_or_else(|_| project_dir.to_path_buf());

    let files = discovery.discover_files(&project_dir, lang.supported_extensions())?;
    let scan_id = db.begin_scan()?;

    info!("discovered {} source files", files.len());

    // Check if config files changed (forces full re-scan for affected languages)
    let force_full = full || config::config_changed(&project_dir, db, lang.config_files());

    // Parse all files, insert symbols, collect imports
    let indexing = indexing::index_files(&project_dir, &files, db, scan_id, force_full, lang)?;

    // Remove stale files that no longer exist on disk
    reconcile::reconcile_deleted_files(&project_dir, &files, db)?;

    // Resolve imports to target file IDs and symbol-level cross-file deps
    let (import_resolution, import_deps) =
        imports::resolve(&project_dir, &indexing.pending_imports, db, lang)?;

    // Resolve unresolved cross-file references using import resolution
    let cross_file_deps = cross_file_deps::resolve_cross_file(
        &indexing.unresolved_refs,
        &indexing.file_import_names,
        &import_resolution,
        db,
    )?;

    let result = ScanResult {
        files_total: indexing.files_total,
        files_skipped: indexing.files_skipped,
        files_partial: indexing.files_partial,
        symbols: indexing.symbols,
        dependencies: indexing.same_file_deps + import_deps + cross_file_deps,
    };

    db.complete_scan(
        scan_id,
        result.files_total,
        result.symbols,
        result.dependencies,
    )?;

    // Save config hash for next incremental scan
    config::save_config_hash(&project_dir, db, lang.config_files());

    info!(
        "scan complete: {} files, {} symbols, {} deps",
        result.files_total, result.symbols, result.dependencies
    );

    Ok(result)
}