use arbor_core::{parse_file, CodeNode};
use arbor_graph::{ArborGraph, GraphBuilder};
use ignore::WalkBuilder;
use std::path::Path;
use std::time::Instant;
use tracing::{debug, info, warn};
pub struct IndexResult {
pub graph: ArborGraph,
pub files_indexed: usize,
pub nodes_extracted: usize,
pub duration_ms: u64,
pub errors: Vec<(String, String)>,
}
pub fn index_directory(root: &Path) -> Result<IndexResult, std::io::Error> {
let start = Instant::now();
let mut builder = GraphBuilder::new();
let mut files_indexed = 0;
let mut nodes_extracted = 0;
let mut errors = Vec::new();
info!("Starting index of {}", root.display());
let walker = WalkBuilder::new(root)
.hidden(true) .git_ignore(true) .git_global(true)
.git_exclude(true)
.build();
for entry in walker.filter_map(Result::ok) {
let path = entry.path();
if path.is_dir() {
continue;
}
let extension = match path.extension().and_then(|e| e.to_str()) {
Some(ext) => ext,
None => continue,
};
if !arbor_core::languages::is_supported(extension) {
continue;
}
debug!("Parsing {}", path.display());
match parse_file(path) {
Ok(nodes) => {
nodes_extracted += nodes.len();
files_indexed += 1;
builder.add_nodes(nodes);
}
Err(e) => {
warn!("Failed to parse {}: {}", path.display(), e);
errors.push((path.display().to_string(), e.to_string()));
}
}
}
let graph = builder.build();
let duration = start.elapsed();
info!(
"Indexed {} files ({} nodes) in {:?}",
files_indexed, nodes_extracted, duration
);
Ok(IndexResult {
graph,
files_indexed,
nodes_extracted,
duration_ms: duration.as_millis() as u64,
errors,
})
}
#[allow(dead_code)]
pub fn parse_single_file(path: &Path) -> Result<Vec<CodeNode>, arbor_core::ParseError> {
parse_file(path)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_index_empty_directory() {
let dir = tempdir().unwrap();
let result = index_directory(dir.path()).unwrap();
assert_eq!(result.files_indexed, 0);
assert_eq!(result.nodes_extracted, 0);
}
#[test]
fn test_index_with_rust_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.rs");
fs::write(
&file_path,
r#"
pub fn hello() {
println!("Hello!");
}
"#,
)
.unwrap();
let result = index_directory(dir.path()).unwrap();
assert_eq!(result.files_indexed, 1);
assert!(result.nodes_extracted > 0);
}
}