use std::path::{Path, PathBuf};
use std::time::Instant;
use crate::format::AcbWriter;
use crate::graph::CodeGraph;
use crate::parse::parser::{ParseOptions, Parser};
use crate::semantic::analyzer::{AnalyzeOptions, SemanticAnalyzer};
use crate::types::{AcbResult, Language};
#[derive(Debug, Clone)]
pub struct CompileOptions {
pub output: PathBuf,
pub languages: Vec<Language>,
pub exclude_patterns: Vec<String>,
pub include_tests: bool,
pub detect_patterns: bool,
pub extract_concepts: bool,
pub trace_ffi: bool,
}
impl Default for CompileOptions {
fn default() -> Self {
Self {
output: PathBuf::from("graph.acb"),
languages: Vec::new(),
exclude_patterns: Vec::new(),
include_tests: true,
detect_patterns: true,
extract_concepts: true,
trace_ffi: true,
}
}
}
pub struct CompileResult {
pub graph: CodeGraph,
pub stats: CompileStats,
}
#[derive(Debug, Clone)]
pub struct CompileStats {
pub files_parsed: usize,
pub parse_errors: usize,
pub units_created: usize,
pub edges_created: usize,
pub languages: Vec<Language>,
pub duration: std::time::Duration,
}
pub struct CompilePipeline;
impl CompilePipeline {
pub fn new() -> Self {
Self
}
pub fn compile(&self, dir: &Path, opts: &CompileOptions) -> AcbResult<CompileResult> {
let start = Instant::now();
let parse_opts = ParseOptions {
languages: opts.languages.clone(),
exclude: opts.exclude_patterns.clone(),
include_tests: opts.include_tests,
..ParseOptions::default()
};
tracing::info!("Parsing {}", dir.display());
let parser = Parser::new();
let parse_result = parser.parse_directory(dir, &parse_opts)?;
let files_parsed = parse_result.stats.files_parsed;
let parse_errors = parse_result.errors.len();
if !parse_result.errors.is_empty() {
for err in &parse_result.errors {
tracing::warn!("Parse error: {}: {}", err.path.display(), err.message);
}
}
let analyze_opts = AnalyzeOptions {
detect_patterns: opts.detect_patterns,
extract_concepts: opts.extract_concepts,
trace_ffi: opts.trace_ffi,
};
tracing::info!("Analyzing {} units", parse_result.units.len());
let analyzer = SemanticAnalyzer::new();
let graph = analyzer.analyze(parse_result.units, &analyze_opts)?;
let duration = start.elapsed();
let stats = CompileStats {
files_parsed,
parse_errors,
units_created: graph.unit_count(),
edges_created: graph.edge_count(),
languages: graph.languages().iter().copied().collect(),
duration,
};
tracing::info!(
"Compiled {} units, {} edges in {:.2?}",
stats.units_created,
stats.edges_created,
stats.duration
);
Ok(CompileResult { graph, stats })
}
pub fn write(&self, graph: &CodeGraph, output: &Path) -> AcbResult<()> {
tracing::info!("Writing {}", output.display());
if let Some(parent) = output.parent() {
std::fs::create_dir_all(parent)?;
}
let writer = AcbWriter::new(graph.dimension());
writer.write_to_file(graph, output)?;
tracing::info!("Wrote {}", output.display());
Ok(())
}
pub fn compile_and_write(&self, dir: &Path, opts: &CompileOptions) -> AcbResult<CompileResult> {
let result = self.compile(dir, opts)?;
self.write(&result.graph, &opts.output)?;
Ok(result)
}
}
impl Default for CompilePipeline {
fn default() -> Self {
Self::new()
}
}