#[async_trait]
impl LanguageStrategy for PythonStrategy {
fn language(&self) -> Language {
Language::Python
}
fn can_parse(&self, path: &Path) -> bool {
path.extension()
.and_then(|ext| ext.to_str())
.is_some_and(|ext| ext == "py" || ext == "pyi")
}
#[cfg(feature = "python-ast")]
async fn parse_file(&self, _path: &Path, content: &str) -> Result<AstDag> {
let tree = self.parse_with_tree_sitter(content)?;
Ok(self.convert_tree_to_dag(&tree, content))
}
#[cfg(not(feature = "python-ast"))]
async fn parse_file(&self, _path: &Path, _content: &str) -> Result<AstDag> {
Err(anyhow::anyhow!(
"Python AST parsing not available - compile with 'python-ast' feature"
))
}
fn extract_imports(&self, ast: &AstDag) -> Vec<String> {
let mut imports = Vec::new();
for i in 0..ast.nodes.len() {
if let Some(node) = ast.nodes.get(i as u32) {
if matches!(node.kind, AstKind::Import(_)) {
imports.push(format!("import_{i}"));
}
}
}
imports
}
fn extract_functions(&self, ast: &AstDag) -> Vec<UnifiedAstNode> {
let mut functions = Vec::new();
for i in 0..ast.nodes.len() {
if let Some(node) = ast.nodes.get(i as u32) {
if matches!(node.kind, AstKind::Function(_)) {
functions.push(node.clone());
}
}
}
functions
}
fn extract_types(&self, ast: &AstDag) -> Vec<UnifiedAstNode> {
let mut types = Vec::new();
for i in 0..ast.nodes.len() {
if let Some(node) = ast.nodes.get(i as u32) {
if matches!(node.kind, AstKind::Class(_)) {
types.push(node.clone());
}
}
}
types
}
fn calculate_complexity(&self, ast: &AstDag) -> (u32, u32) {
let mut cyclomatic = 1;
let mut cognitive = 0;
for i in 0..ast.nodes.len() {
if let Some(node) = ast.nodes.get(i as u32) {
if node.flags.has(NodeFlags::CONTROL_FLOW) {
cyclomatic += 1;
cognitive += 1;
}
}
}
(cyclomatic, cognitive)
}
}