use crate::error::{Result, SpliceError};
use crate::ingest::{
cpp::CppSymbol, java::JavaSymbol, javascript::JavaScriptSymbol, python::PythonSymbol,
rust::RustSymbol, typescript::TypeScriptSymbol,
};
use std::path::Path;
pub trait Symbol {
fn name(&self) -> &str;
fn kind(&self) -> &str;
fn byte_start(&self) -> usize;
fn byte_end(&self) -> usize;
fn line_start(&self) -> usize;
fn line_end(&self) -> usize;
fn col_start(&self) -> usize;
fn col_end(&self) -> usize;
fn fully_qualified(&self) -> &str;
fn language(&self) -> Language;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Language {
Rust,
Python,
C,
Cpp,
Java,
JavaScript,
TypeScript,
}
impl Language {
pub fn as_str(&self) -> &'static str {
match self {
Language::Rust => "rust",
Language::Python => "python",
Language::C => "c",
Language::Cpp => "cpp",
Language::Java => "java",
Language::JavaScript => "javascript",
Language::TypeScript => "typescript",
}
}
pub fn from_path(path: &Path) -> Option<Self> {
use crate::ingest::detect::detect_language;
detect_language(path).map(|lang| match lang {
crate::ingest::detect::Language::Rust => Language::Rust,
crate::ingest::detect::Language::Python => Language::Python,
crate::ingest::detect::Language::C => Language::C,
crate::ingest::detect::Language::Cpp => Language::Cpp,
crate::ingest::detect::Language::Java => Language::Java,
crate::ingest::detect::Language::JavaScript => Language::JavaScript,
crate::ingest::detect::Language::TypeScript => Language::TypeScript,
})
}
}
pub fn parser_for_language(language: Language) -> Result<tree_sitter::Parser> {
let mut parser = tree_sitter::Parser::new();
let lang = match language {
Language::Rust => tree_sitter_rust::language(),
Language::Python => tree_sitter_python::language(),
Language::C => tree_sitter_c::language(),
Language::Cpp => tree_sitter_cpp::language(),
Language::Java => tree_sitter_java::language(),
Language::JavaScript => tree_sitter_javascript::language(),
Language::TypeScript => tree_sitter_typescript::language_typescript(),
};
parser.set_language(&lang).map_err(|e| SpliceError::Parse {
file: std::path::PathBuf::from("<unknown>"),
message: format!("Failed to set language for parser: {:?}", e),
})?;
Ok(parser)
}
#[derive(Debug, Clone)]
pub enum AnySymbol {
Rust(RustSymbol),
Python(PythonSymbol),
Cpp(CppSymbol),
Java(JavaSymbol),
JavaScript(JavaScriptSymbol),
TypeScript(TypeScriptSymbol),
}
impl Symbol for AnySymbol {
fn name(&self) -> &str {
match self {
AnySymbol::Rust(s) => s.name.as_str(),
AnySymbol::Python(s) => s.name.as_str(),
AnySymbol::Cpp(s) => s.name.as_str(),
AnySymbol::Java(s) => s.name.as_str(),
AnySymbol::JavaScript(s) => s.name.as_str(),
AnySymbol::TypeScript(s) => s.name.as_str(),
}
}
fn kind(&self) -> &str {
match self {
AnySymbol::Rust(s) => s.kind.as_str(),
AnySymbol::Python(s) => s.kind.as_str(),
AnySymbol::Cpp(s) => s.kind.as_str(),
AnySymbol::Java(s) => s.kind.as_str(),
AnySymbol::JavaScript(s) => s.kind.as_str(),
AnySymbol::TypeScript(s) => s.kind.as_str(),
}
}
fn byte_start(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.byte_start,
AnySymbol::Python(s) => s.byte_start,
AnySymbol::Cpp(s) => s.byte_start,
AnySymbol::Java(s) => s.byte_start,
AnySymbol::JavaScript(s) => s.byte_start,
AnySymbol::TypeScript(s) => s.byte_start,
}
}
fn byte_end(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.byte_end,
AnySymbol::Python(s) => s.byte_end,
AnySymbol::Cpp(s) => s.byte_end,
AnySymbol::Java(s) => s.byte_end,
AnySymbol::JavaScript(s) => s.byte_end,
AnySymbol::TypeScript(s) => s.byte_end,
}
}
fn line_start(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.line_start,
AnySymbol::Python(s) => s.line_start,
AnySymbol::Cpp(s) => s.line_start,
AnySymbol::Java(s) => s.line_start,
AnySymbol::JavaScript(s) => s.line_start,
AnySymbol::TypeScript(s) => s.line_start,
}
}
fn line_end(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.line_end,
AnySymbol::Python(s) => s.line_end,
AnySymbol::Cpp(s) => s.line_end,
AnySymbol::Java(s) => s.line_end,
AnySymbol::JavaScript(s) => s.line_end,
AnySymbol::TypeScript(s) => s.line_end,
}
}
fn col_start(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.col_start,
AnySymbol::Python(s) => s.col_start,
AnySymbol::Cpp(s) => s.col_start,
AnySymbol::Java(s) => s.col_start,
AnySymbol::JavaScript(s) => s.col_start,
AnySymbol::TypeScript(s) => s.col_end,
}
}
fn col_end(&self) -> usize {
match self {
AnySymbol::Rust(s) => s.col_end,
AnySymbol::Python(s) => s.col_end,
AnySymbol::Cpp(s) => s.col_end,
AnySymbol::Java(s) => s.col_end,
AnySymbol::JavaScript(s) => s.col_end,
AnySymbol::TypeScript(s) => s.col_end,
}
}
fn fully_qualified(&self) -> &str {
match self {
AnySymbol::Rust(s) => s.fully_qualified.as_str(),
AnySymbol::Python(s) => s.fully_qualified.as_str(),
AnySymbol::Cpp(s) => s.fully_qualified.as_str(),
AnySymbol::Java(s) => s.fully_qualified.as_str(),
AnySymbol::JavaScript(s) => s.fully_qualified.as_str(),
AnySymbol::TypeScript(s) => s.fully_qualified.as_str(),
}
}
fn language(&self) -> Language {
match self {
AnySymbol::Rust(_) => Language::Rust,
AnySymbol::Python(_) => Language::Python,
AnySymbol::Cpp(_) => Language::Cpp,
AnySymbol::Java(_) => Language::Java,
AnySymbol::JavaScript(_) => Language::JavaScript,
AnySymbol::TypeScript(_) => Language::TypeScript,
}
}
}
impl Symbol for RustSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::Rust
}
}
impl Symbol for PythonSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::Python
}
}
impl Symbol for CppSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::Cpp
}
}
impl Symbol for JavaSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::Java
}
}
impl Symbol for JavaScriptSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::JavaScript
}
}
impl Symbol for TypeScriptSymbol {
fn name(&self) -> &str {
self.name.as_str()
}
fn kind(&self) -> &str {
self.kind.as_str()
}
fn byte_start(&self) -> usize {
self.byte_start
}
fn byte_end(&self) -> usize {
self.byte_end
}
fn line_start(&self) -> usize {
self.line_start
}
fn line_end(&self) -> usize {
self.line_end
}
fn col_start(&self) -> usize {
self.col_start
}
fn col_end(&self) -> usize {
self.col_end
}
fn fully_qualified(&self) -> &str {
self.fully_qualified.as_str()
}
fn language(&self) -> Language {
Language::TypeScript
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_language_as_str() {
assert_eq!(Language::Rust.as_str(), "rust");
assert_eq!(Language::Python.as_str(), "python");
assert_eq!(Language::Cpp.as_str(), "cpp");
assert_eq!(Language::Java.as_str(), "java");
assert_eq!(Language::JavaScript.as_str(), "javascript");
assert_eq!(Language::TypeScript.as_str(), "typescript");
}
#[test]
fn test_language_from_path() {
use std::path::Path;
assert_eq!(
Language::from_path(Path::new("main.rs")),
Some(Language::Rust)
);
assert_eq!(
Language::from_path(Path::new("script.py")),
Some(Language::Python)
);
assert_eq!(
Language::from_path(Path::new("main.cpp")),
Some(Language::Cpp)
);
assert_eq!(
Language::from_path(Path::new("Main.java")),
Some(Language::Java)
);
assert_eq!(
Language::from_path(Path::new("test.js")),
Some(Language::JavaScript)
);
assert_eq!(
Language::from_path(Path::new("test.ts")),
Some(Language::TypeScript)
);
assert_eq!(Language::from_path(Path::new("file.txt")), None);
}
}