acp/ast/
mod.rs

1//! @acp:module "AST Parsing"
2//! @acp:summary "Tree-sitter based AST parsing for symbol extraction"
3//! @acp:domain cli
4//! @acp:layer parsing
5//!
6//! # AST Parsing
7//!
8//! Provides tree-sitter based AST parsing for extracting symbols from source code:
9//! - Functions, methods, classes, structs
10//! - Interfaces, traits, enums, type aliases
11//! - Import/export statements
12//! - Function calls for call graph analysis
13
14pub mod languages;
15pub mod parser;
16
17pub use languages::{get_extractor, LanguageExtractor};
18pub use parser::AstParser;
19
20use serde::{Deserialize, Serialize};
21
22/// Symbol kinds extracted from AST
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub enum SymbolKind {
26    /// A function declaration
27    Function,
28    /// A method (function inside a class/struct/impl)
29    Method,
30    /// A class declaration
31    Class,
32    /// A struct declaration
33    Struct,
34    /// An interface declaration
35    Interface,
36    /// A trait declaration (Rust)
37    Trait,
38    /// An enum declaration
39    Enum,
40    /// An enum variant/member
41    EnumVariant,
42    /// A constant declaration
43    Constant,
44    /// A variable declaration
45    Variable,
46    /// A type alias
47    TypeAlias,
48    /// A module declaration
49    Module,
50    /// A namespace declaration
51    Namespace,
52    /// A property (in class/object)
53    Property,
54    /// A field (in struct)
55    Field,
56    /// An impl block (Rust)
57    Impl,
58}
59
60impl std::fmt::Display for SymbolKind {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            Self::Function => write!(f, "function"),
64            Self::Method => write!(f, "method"),
65            Self::Class => write!(f, "class"),
66            Self::Struct => write!(f, "struct"),
67            Self::Interface => write!(f, "interface"),
68            Self::Trait => write!(f, "trait"),
69            Self::Enum => write!(f, "enum"),
70            Self::EnumVariant => write!(f, "enum_variant"),
71            Self::Constant => write!(f, "constant"),
72            Self::Variable => write!(f, "variable"),
73            Self::TypeAlias => write!(f, "type_alias"),
74            Self::Module => write!(f, "module"),
75            Self::Namespace => write!(f, "namespace"),
76            Self::Property => write!(f, "property"),
77            Self::Field => write!(f, "field"),
78            Self::Impl => write!(f, "impl"),
79        }
80    }
81}
82
83/// Visibility of a symbol
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
85#[serde(rename_all = "snake_case")]
86pub enum Visibility {
87    /// Public visibility (default for most languages)
88    #[default]
89    Public,
90    /// Private visibility
91    Private,
92    /// Protected visibility (class inheritance)
93    Protected,
94    /// Internal visibility (package/module level)
95    Internal,
96    /// Crate-level visibility (Rust pub(crate))
97    Crate,
98}
99
100/// A function/method parameter
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct Parameter {
103    /// Parameter name
104    pub name: String,
105    /// Type annotation (if available)
106    pub type_info: Option<String>,
107    /// Default value (if available)
108    pub default_value: Option<String>,
109    /// Is this a rest/variadic parameter?
110    pub is_rest: bool,
111    /// Is this optional? (TypeScript)
112    pub is_optional: bool,
113}
114
115/// A symbol extracted from AST parsing
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ExtractedSymbol {
118    /// Symbol name (e.g., "main", "UserService")
119    pub name: String,
120    /// Fully qualified name (e.g., "src/main.rs::main")
121    pub qualified_name: Option<String>,
122    /// Symbol kind
123    pub kind: SymbolKind,
124    /// Start line (1-indexed)
125    pub start_line: usize,
126    /// End line (1-indexed)
127    pub end_line: usize,
128    /// Start column (0-indexed)
129    pub start_col: usize,
130    /// End column (0-indexed)
131    pub end_col: usize,
132    /// Function/method signature (for display)
133    pub signature: Option<String>,
134    /// Visibility modifier
135    pub visibility: Visibility,
136    /// Doc comment (if present)
137    pub doc_comment: Option<String>,
138    /// Parent symbol name (for nested symbols)
139    pub parent: Option<String>,
140    /// Type information (return type or declared type)
141    pub type_info: Option<String>,
142    /// Function/method parameters
143    pub parameters: Vec<Parameter>,
144    /// Return type (if available)
145    pub return_type: Option<String>,
146    /// Is this exported/public?
147    pub exported: bool,
148    /// Is this async?
149    pub is_async: bool,
150    /// Is this static?
151    pub is_static: bool,
152    /// Generic type parameters
153    pub generics: Vec<String>,
154}
155
156impl ExtractedSymbol {
157    /// Create a new ExtractedSymbol with required fields
158    pub fn new(name: String, kind: SymbolKind, start_line: usize, end_line: usize) -> Self {
159        Self {
160            name,
161            qualified_name: None,
162            kind,
163            start_line,
164            end_line,
165            start_col: 0,
166            end_col: 0,
167            signature: None,
168            visibility: Visibility::default(),
169            doc_comment: None,
170            parent: None,
171            type_info: None,
172            parameters: Vec::new(),
173            return_type: None,
174            exported: false,
175            is_async: false,
176            is_static: false,
177            generics: Vec::new(),
178        }
179    }
180
181    /// Set the qualified name
182    pub fn with_qualified_name(mut self, name: impl Into<String>) -> Self {
183        self.qualified_name = Some(name.into());
184        self
185    }
186
187    /// Set column positions
188    pub fn with_columns(mut self, start_col: usize, end_col: usize) -> Self {
189        self.start_col = start_col;
190        self.end_col = end_col;
191        self
192    }
193
194    /// Set the signature
195    pub fn with_signature(mut self, sig: impl Into<String>) -> Self {
196        self.signature = Some(sig.into());
197        self
198    }
199
200    /// Set visibility
201    pub fn with_visibility(mut self, vis: Visibility) -> Self {
202        self.visibility = vis;
203        self
204    }
205
206    /// Set doc comment
207    pub fn with_doc_comment(mut self, doc: impl Into<String>) -> Self {
208        self.doc_comment = Some(doc.into());
209        self
210    }
211
212    /// Set parent
213    pub fn with_parent(mut self, parent: impl Into<String>) -> Self {
214        self.parent = Some(parent.into());
215        self
216    }
217
218    /// Set return type
219    pub fn with_return_type(mut self, ret: impl Into<String>) -> Self {
220        self.return_type = Some(ret.into());
221        self
222    }
223
224    /// Mark as exported
225    pub fn exported(mut self) -> Self {
226        self.exported = true;
227        self
228    }
229
230    /// Mark as async
231    pub fn async_fn(mut self) -> Self {
232        self.is_async = true;
233        self
234    }
235
236    /// Mark as static
237    pub fn static_fn(mut self) -> Self {
238        self.is_static = true;
239        self
240    }
241
242    /// Add a parameter
243    pub fn add_parameter(&mut self, param: Parameter) {
244        self.parameters.push(param);
245    }
246
247    /// Add a generic type parameter
248    pub fn add_generic(&mut self, generic: impl Into<String>) {
249        self.generics.push(generic.into());
250    }
251}
252
253/// An import statement extracted from AST
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct Import {
256    /// Source module path
257    pub source: String,
258    /// Imported names (empty for default/namespace imports)
259    pub names: Vec<ImportedName>,
260    /// Is this a default import?
261    pub is_default: bool,
262    /// Is this a namespace import (import * as x)?
263    pub is_namespace: bool,
264    /// Line number
265    pub line: usize,
266}
267
268/// A single imported name
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct ImportedName {
271    /// Original name
272    pub name: String,
273    /// Alias (if renamed)
274    pub alias: Option<String>,
275}
276
277/// A function call for call graph analysis
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct FunctionCall {
280    /// Name of the function containing this call
281    pub caller: String,
282    /// Name of the function being called
283    pub callee: String,
284    /// Line number of the call
285    pub line: usize,
286    /// Is this a method call (x.foo())?
287    pub is_method: bool,
288    /// Receiver expression (for method calls)
289    pub receiver: Option<String>,
290}