pub mod go_imports;
pub mod go_symbols;
pub mod imports;
pub mod languages;
pub mod python_imports;
pub mod python_symbols;
pub mod relationships;
pub mod symbols;
use std::cell::RefCell;
use std::path::Path;
use anyhow::{Context, Result, anyhow};
use tree_sitter::Parser;
use crate::graph::node::SymbolInfo;
use go_imports::extract_go_imports;
use go_symbols::extract_go_symbols;
use imports::{ExportInfo, ImportInfo, extract_exports, extract_imports, extract_rust_use};
use languages::language_for_extension;
use python_imports::extract_python_imports;
use python_symbols::extract_python_symbols;
use relationships::{RelationshipInfo, extract_relationships};
use symbols::{extract_impl_methods, extract_rust_symbols, extract_symbols};
thread_local! {
static PARSER_TS: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()).unwrap();
p
});
static PARSER_TSX: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_typescript::LANGUAGE_TSX.into()).unwrap();
p
});
static PARSER_JS: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_javascript::LANGUAGE.into()).unwrap();
p
});
pub static PARSER_RS: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_rust::LANGUAGE.into()).unwrap();
p
});
static PARSER_PY: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_python::LANGUAGE.into()).unwrap();
p
});
static PARSER_GO: RefCell<Parser> = RefCell::new({
let mut p = Parser::new();
p.set_language(&tree_sitter_go::LANGUAGE.into()).unwrap();
p
});
}
pub struct RustUseInfo {
pub path: String,
pub is_pub_use: bool,
}
pub struct ParseResult {
pub symbols: Vec<(SymbolInfo, Vec<SymbolInfo>)>,
pub imports: Vec<ImportInfo>,
pub exports: Vec<ExportInfo>,
pub relationships: Vec<RelationshipInfo>,
pub rust_uses: Vec<RustUseInfo>,
}
pub fn parse_file(path: &Path, source: &[u8]) -> Result<ParseResult> {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
if ext == "go" {
let language = language_for_extension("go").expect("go language is always Some");
let mut parser = Parser::new();
parser
.set_language(&language)
.with_context(|| "failed to set tree-sitter language for extension \"go\"")?;
let tree = parser
.parse(source, None)
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let symbols = extract_go_symbols(&tree, source, &language);
let imports = extract_go_imports(&tree, source);
return Ok(ParseResult {
symbols,
imports,
exports: Vec::new(),
relationships: Vec::new(),
rust_uses: Vec::new(),
});
}
if ext == "py" {
let language = language_for_extension("py").expect("py language is always Some");
let mut parser = Parser::new();
parser
.set_language(&language)
.with_context(|| "failed to set tree-sitter language for extension \"py\"")?;
let tree = parser
.parse(source, None)
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let symbols = extract_python_symbols(&tree, source, &language);
let imports = extract_python_imports(&tree, source);
return Ok(ParseResult {
symbols,
imports,
exports: Vec::new(),
relationships: Vec::new(),
rust_uses: Vec::new(),
});
}
if ext == "rs" {
let language = language_for_extension("rs").expect("rs language is always Some");
let mut parser = Parser::new();
parser
.set_language(&language)
.with_context(|| "failed to set tree-sitter language for extension \"rs\"")?;
let tree = parser
.parse(source, None)
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let top_level = extract_rust_symbols(&tree, source, &language);
let impl_methods = extract_impl_methods(&tree, source);
let rust_uses = extract_rust_use(&tree, source);
let mut all_symbols = top_level;
all_symbols.extend(impl_methods);
return Ok(ParseResult {
symbols: all_symbols,
imports: Vec::new(),
exports: Vec::new(),
relationships: Vec::new(),
rust_uses,
});
}
let language = language_for_extension(ext)
.ok_or_else(|| anyhow!("unsupported file extension: {:?}", ext))?;
let is_tsx = matches!(ext, "tsx" | "jsx");
let mut parser = Parser::new();
parser
.set_language(&language)
.with_context(|| format!("failed to set tree-sitter language for extension {:?}", ext))?;
let tree = parser
.parse(source, None)
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let symbols = extract_symbols(&tree, source, &language, is_tsx);
let imports = extract_imports(&tree, source, &language, is_tsx);
let exports = extract_exports(&tree, source, &language, is_tsx);
let relationships_vec = extract_relationships(&tree, source, &language, is_tsx);
Ok(ParseResult {
symbols,
imports,
exports,
relationships: relationships_vec,
rust_uses: Vec::new(),
})
}
pub fn parse_file_parallel(path: &Path, source: &[u8]) -> Result<ParseResult> {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
let is_tsx = matches!(ext, "tsx" | "jsx");
if ext == "go" {
let language = language_for_extension("go").expect("go language is always Some");
let tree = PARSER_GO
.with(|p| p.borrow_mut().parse(source, None))
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let symbols = extract_go_symbols(&tree, source, &language);
let imports = extract_go_imports(&tree, source);
return Ok(ParseResult {
symbols,
imports,
exports: Vec::new(),
relationships: Vec::new(),
rust_uses: Vec::new(),
});
}
if ext == "py" {
let language = language_for_extension("py").expect("py language is always Some");
let tree = PARSER_PY
.with(|p| p.borrow_mut().parse(source, None))
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let symbols = extract_python_symbols(&tree, source, &language);
let imports = extract_python_imports(&tree, source);
return Ok(ParseResult {
symbols,
imports,
exports: Vec::new(),
relationships: Vec::new(),
rust_uses: Vec::new(),
});
}
if ext == "rs" {
let language = language_for_extension("rs").expect("rs language is always Some");
let tree = PARSER_RS
.with(|p| p.borrow_mut().parse(source, None))
.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let top_level = extract_rust_symbols(&tree, source, &language);
let impl_methods = extract_impl_methods(&tree, source);
let rust_uses = extract_rust_use(&tree, source);
let mut all_symbols = top_level;
all_symbols.extend(impl_methods);
return Ok(ParseResult {
symbols: all_symbols,
imports: Vec::new(),
exports: Vec::new(),
relationships: Vec::new(),
rust_uses,
});
}
let tree = match ext {
"ts" => PARSER_TS.with(|p| p.borrow_mut().parse(source, None)),
"tsx" => PARSER_TSX.with(|p| p.borrow_mut().parse(source, None)),
"js" | "jsx" => PARSER_JS.with(|p| p.borrow_mut().parse(source, None)),
_ => return Err(anyhow!("unsupported file extension: {:?}", ext)),
};
let tree = tree.ok_or_else(|| anyhow!("tree-sitter returned None for {:?}", path))?;
let language = language_for_extension(ext)
.ok_or_else(|| anyhow!("unsupported file extension: {:?}", ext))?;
let symbols = extract_symbols(&tree, source, &language, is_tsx);
let imports = extract_imports(&tree, source, &language, is_tsx);
let exports = extract_exports(&tree, source, &language, is_tsx);
let relationships_vec = extract_relationships(&tree, source, &language, is_tsx);
Ok(ParseResult {
symbols,
imports,
exports,
relationships: relationships_vec,
rust_uses: Vec::new(),
})
}