use std::path::Path;
use schemars::JsonSchema;
use serde::Deserialize;
use graphyn_adapter_ts::analyze_files;
use graphyn_adapter_ts::language::is_supported_source_file;
use graphyn_core::graph::GraphynGraph;
use graphyn_core::ir::RepoIR;
use graphyn_core::resolver::AliasResolver;
use graphyn_core::scan::{parse_csv_patterns, walk_source_files_with_config, ScanConfig};
use graphyn_store::RocksGraphStore;
#[derive(Debug, Deserialize, JsonSchema)]
pub struct RefreshGraphParams {
pub path: Option<String>,
pub include: Option<String>,
pub exclude: Option<String>,
pub respect_gitignore: Option<bool>,
}
#[derive(Debug, Clone)]
pub struct RefreshResult {
pub symbols: usize,
pub relationships: usize,
pub alias_chains: usize,
pub files_indexed: usize,
pub parse_errors: usize,
}
pub fn execute(
repo_root: &Path,
params: RefreshGraphParams,
) -> Result<(GraphynGraph, RefreshResult), String> {
let analysis_root = match params.path.as_deref() {
Some(rel) if !rel.trim().is_empty() => repo_root.join(rel),
_ => repo_root.to_path_buf(),
};
let root = std::fs::canonicalize(&analysis_root)
.map_err(|e| format!("cannot access '{}': {e}", analysis_root.display()))?;
let scan_config = ScanConfig {
include_patterns: parse_csv_patterns(params.include.as_deref()),
exclude_patterns: parse_csv_patterns(params.exclude.as_deref()),
respect_gitignore: params.respect_gitignore.unwrap_or(true),
};
let files = walk_source_files_with_config(&root, &scan_config, is_supported_source_file)
.map_err(|e| format!("scan failed: {e}"))?;
let repo_ir = analyze_files(&root, &files).map_err(|e| format!("analysis failed: {e}"))?;
let parse_errors: usize = repo_ir.files.iter().map(|f| f.parse_errors.len()).sum();
let graph = build_graph(&repo_ir);
let db = root.join(".graphyn").join("db");
if let Some(parent) = db.parent() {
std::fs::create_dir_all(parent).map_err(|e| format!("failed to create db dir: {e}"))?;
}
let store = RocksGraphStore::open(&db).map_err(|e| format!("failed to open store: {e}"))?;
store
.save_graph(&graph)
.map_err(|e| format!("failed to save graph: {e}"))?;
let result = RefreshResult {
symbols: graph.symbols.len(),
relationships: graph.graph.edge_count(),
alias_chains: graph.alias_chains.len(),
files_indexed: repo_ir.files.len(),
parse_errors,
};
Ok((graph, result))
}
fn build_graph(repo_ir: &RepoIR) -> GraphynGraph {
let mut graph = GraphynGraph::new();
let resolver = AliasResolver::default();
for file_ir in &repo_ir.files {
for symbol in &file_ir.symbols {
graph.add_symbol(symbol.clone());
}
}
for file_ir in &repo_ir.files {
for relationship in &file_ir.relationships {
graph.add_relationship(relationship);
}
resolver.ingest_relationships(&graph, &file_ir.relationships);
}
graph
}