Skip to main content

agentic_codebase/engine/
compile.rs

1//! Main compilation pipeline.
2//!
3//! Orchestrates the full parse -> analyze -> build graph -> write `.acb` pipeline.
4//! This is the central entry point used by both the CLI (`/compile`) and
5//! programmatic callers.
6
7use std::path::{Path, PathBuf};
8use std::time::Instant;
9
10use crate::format::AcbWriter;
11use crate::graph::CodeGraph;
12use crate::parse::parser::{ParseOptions, Parser};
13use crate::semantic::analyzer::{AnalyzeOptions, SemanticAnalyzer};
14use crate::types::{AcbResult, Language};
15
16/// Options for the compilation pipeline.
17#[derive(Debug, Clone)]
18pub struct CompileOptions {
19    /// Output path for the `.acb` file.
20    pub output: PathBuf,
21    /// Languages to include (empty = all supported).
22    pub languages: Vec<Language>,
23    /// Glob patterns to exclude from scanning.
24    pub exclude_patterns: Vec<String>,
25    /// Include test files in the graph.
26    pub include_tests: bool,
27    /// Detect design patterns during analysis.
28    pub detect_patterns: bool,
29    /// Extract high-level concepts.
30    pub extract_concepts: bool,
31    /// Trace FFI boundaries.
32    pub trace_ffi: bool,
33}
34
35impl Default for CompileOptions {
36    fn default() -> Self {
37        Self {
38            output: PathBuf::from("graph.acb"),
39            languages: Vec::new(),
40            exclude_patterns: Vec::new(),
41            include_tests: true,
42            detect_patterns: true,
43            extract_concepts: true,
44            trace_ffi: true,
45        }
46    }
47}
48
49/// Result of a compilation run.
50pub struct CompileResult {
51    /// The compiled code graph.
52    pub graph: CodeGraph,
53    /// Compilation statistics.
54    pub stats: CompileStats,
55}
56
57/// Statistics from a compilation run.
58#[derive(Debug, Clone)]
59pub struct CompileStats {
60    /// Number of source files parsed.
61    pub files_parsed: usize,
62    /// Number of parse errors encountered.
63    pub parse_errors: usize,
64    /// Number of code units in the graph.
65    pub units_created: usize,
66    /// Number of edges in the graph.
67    pub edges_created: usize,
68    /// Languages found in the codebase.
69    pub languages: Vec<Language>,
70    /// Total compilation duration.
71    pub duration: std::time::Duration,
72}
73
74/// The compilation pipeline.
75pub struct CompilePipeline;
76
77impl CompilePipeline {
78    /// Create a new compilation pipeline.
79    pub fn new() -> Self {
80        Self
81    }
82
83    /// Compile a directory into a code graph.
84    ///
85    /// Runs the full pipeline: parse -> analyze -> build graph.
86    /// Does NOT write the `.acb` file -- call [`write`] separately.
87    pub fn compile(&self, dir: &Path, opts: &CompileOptions) -> AcbResult<CompileResult> {
88        let start = Instant::now();
89
90        // Phase 1: Parse source files
91        let parse_opts = ParseOptions {
92            languages: opts.languages.clone(),
93            exclude: opts.exclude_patterns.clone(),
94            include_tests: opts.include_tests,
95            ..ParseOptions::default()
96        };
97
98        tracing::info!("Parsing {}", dir.display());
99        let parser = Parser::new();
100        let parse_result = parser.parse_directory(dir, &parse_opts)?;
101
102        let files_parsed = parse_result.stats.files_parsed;
103        let parse_errors = parse_result.errors.len();
104
105        if !parse_result.errors.is_empty() {
106            for err in &parse_result.errors {
107                tracing::warn!("Parse error: {}: {}", err.path.display(), err.message);
108            }
109        }
110
111        // Phase 2: Semantic analysis
112        let analyze_opts = AnalyzeOptions {
113            detect_patterns: opts.detect_patterns,
114            extract_concepts: opts.extract_concepts,
115            trace_ffi: opts.trace_ffi,
116        };
117
118        tracing::info!("Analyzing {} units", parse_result.units.len());
119        let analyzer = SemanticAnalyzer::new();
120        let graph = analyzer.analyze(parse_result.units, &analyze_opts)?;
121
122        let duration = start.elapsed();
123
124        let stats = CompileStats {
125            files_parsed,
126            parse_errors,
127            units_created: graph.unit_count(),
128            edges_created: graph.edge_count(),
129            languages: graph.languages().iter().copied().collect(),
130            duration,
131        };
132
133        tracing::info!(
134            "Compiled {} units, {} edges in {:.2?}",
135            stats.units_created,
136            stats.edges_created,
137            stats.duration
138        );
139
140        Ok(CompileResult { graph, stats })
141    }
142
143    /// Write a compiled graph to an `.acb` file.
144    pub fn write(&self, graph: &CodeGraph, output: &Path) -> AcbResult<()> {
145        tracing::info!("Writing {}", output.display());
146
147        if let Some(parent) = output.parent() {
148            std::fs::create_dir_all(parent)?;
149        }
150
151        let writer = AcbWriter::new(graph.dimension());
152        writer.write_to_file(graph, output)?;
153
154        tracing::info!("Wrote {}", output.display());
155        Ok(())
156    }
157
158    /// Compile and write in one step.
159    pub fn compile_and_write(&self, dir: &Path, opts: &CompileOptions) -> AcbResult<CompileResult> {
160        let result = self.compile(dir, opts)?;
161        self.write(&result.graph, &opts.output)?;
162        Ok(result)
163    }
164}
165
166impl Default for CompilePipeline {
167    fn default() -> Self {
168        Self::new()
169    }
170}