use std::fmt;
use tree_sitter::{Language, Parser, Tree};
use tree_sitter_c_sharp::LANGUAGE as C_SHARP_LANGUAGE;
use tree_sitter_erlang::LANGUAGE as ERLANG_LANGUAGE;
use tree_sitter_go::LANGUAGE as GO_LANGUAGE;
use tree_sitter_java::LANGUAGE as JAVA_LANGUAGE;
use tree_sitter_javascript::LANGUAGE as JAVASCRIPT_LANGUAGE;
use tree_sitter_python::LANGUAGE as PYTHON_LANGUAGE;
use tree_sitter_rust::LANGUAGE as RUST_LANGUAGE;
use tree_sitter_typescript::{LANGUAGE_TSX, LANGUAGE_TYPESCRIPT};
pub mod go_resolve;
pub mod go_stdlib;
pub mod python_stdlib;
pub mod python_common_external;
pub mod scanner;
pub mod scanner_incremental;
pub mod compress;
pub mod graph;
pub mod pipeline;
pub mod schema;
pub mod edge;
pub mod ir;
pub mod extract;
pub mod store;
pub mod project;
pub use graph::ExtractOptions;
pub use graph::{build_project_ir, enrich_project_ir_code_bytes};
pub use extract::scan_and_build_ir_async;
pub use project::{parse_project, parse_project_async, refresh_files, ProjectError};
pub use compress::{
compressor_language_from_ir_string, decompress_code_bytes, language_id_from_ir_string,
CompressorClient, CompressorConfig, CompressError,
};
pub use store::{
ExplainOptions, ExplainSourceOrigin, ExplainSymbolResult, GraphStore, InMemoryGraph,
ImpactReport, QueryLimits, SymbolRef,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum LanguageId {
Java, JavaScript, TypeScript, Tsx, Python, Rust, Go, Erlang, CSharp, }
impl fmt::Display for LanguageId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
LanguageId::Java => "java",
LanguageId::JavaScript => "javascript",
LanguageId::TypeScript => "typescript",
LanguageId::Tsx => "tsx",
LanguageId::Python => "python",
LanguageId::Rust => "rust",
LanguageId::Go => "go",
LanguageId::Erlang => "erlang",
LanguageId::CSharp => "c_sharp",
};
f.write_str(s)
}
}
#[derive(Debug, thiserror::Error)]
pub enum ParserError {
#[error("unsupported language: {0}")]
UnsupportedLanguage(String),
#[error("failed to set Tree-Sitter language: {0}")]
SetLanguage(String),
#[error("failed to parse source for language {language}")]
ParseFailed { language: LanguageId },
}
pub fn language_for(id: LanguageId) -> Result<Language, ParserError> {
let lang: Language = match id {
LanguageId::Java => JAVA_LANGUAGE.into(),
LanguageId::JavaScript => JAVASCRIPT_LANGUAGE.into(),
LanguageId::TypeScript => LANGUAGE_TYPESCRIPT.into(),
LanguageId::Tsx => LANGUAGE_TSX.into(),
LanguageId::Python => PYTHON_LANGUAGE.into(),
LanguageId::Rust => RUST_LANGUAGE.into(),
LanguageId::Go => GO_LANGUAGE.into(),
LanguageId::Erlang => ERLANG_LANGUAGE.into(),
LanguageId::CSharp => C_SHARP_LANGUAGE.into(),
};
Ok(lang)
}
pub struct MultiLangParser {
parser: Parser,
current_language: Option<LanguageId>,
}
impl MultiLangParser {
pub fn new() -> Result<Self, ParserError> {
Ok(Self {
parser: Parser::new(),
current_language: None,
})
}
fn ensure_language(&mut self, lang_id: LanguageId) -> Result<(), ParserError> {
if self.current_language == Some(lang_id) {
return Ok(());
}
let lang = language_for(lang_id)?;
self.parser
.set_language(&lang)
.map_err(|e| ParserError::SetLanguage(e.to_string()))?;
self.current_language = Some(lang_id);
Ok(())
}
pub fn parse_source(
&mut self,
lang_id: LanguageId,
source: &str,
) -> Result<Tree, ParserError> {
self.ensure_language(lang_id)?;
self.parser
.parse(source, None)
.ok_or(ParserError::ParseFailed { language: lang_id })
}
}
pub fn parse_once(lang_id: LanguageId, source: &str) -> Result<Tree, ParserError> {
let mut parser = MultiLangParser::new()?;
parser.parse_source(lang_id, source)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_simple_java() {
let src = r#"class A { void m() {} }"#;
let tree = parse_once(LanguageId::Java, src).expect("java parse failed");
let root = tree.root_node();
assert!(root.child_count() > 0);
}
}