pub mod rust_parser;
pub mod python_parser;
pub mod go_parser;
pub mod typescript_parser;
pub mod java_parser;
pub mod c_parser;
pub mod cpp_parser;
pub mod ruby_parser;
#[cfg(feature = "pattern")]
pub mod pattern;
#[cfg(feature = "text-search")]
pub mod text_search;
use deagle_core::{Language, Node, Result};
use std::path::Path;
pub use rust_parser::ParseResult;
pub(crate) fn truncate_content(s: &str, max_bytes: usize) -> String {
if s.len() <= max_bytes {
return s.to_string();
}
let mut end = max_bytes;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
format!("{}...", &s[..end])
}
pub fn parse_file(path: &Path, content: &str, language: Language) -> Result<Vec<Node>> {
match language {
Language::Rust => rust_parser::parse(path, content),
Language::Python => python_parser::parse(path, content),
Language::Go => go_parser::parse(path, content),
Language::TypeScript | Language::JavaScript => typescript_parser::parse(path, content),
Language::Java => java_parser::parse(path, content),
Language::C => c_parser::parse(path, content),
Language::Cpp => cpp_parser::parse(path, content),
Language::Ruby => ruby_parser::parse(path, content),
_ => Ok(Vec::new()),
}
}
pub fn parse_file_with_edges(path: &Path, content: &str, language: Language) -> Result<ParseResult> {
match language {
Language::Rust => rust_parser::parse_with_edges(path, content),
Language::Python => python_parser::parse_with_edges(path, content),
Language::Go => go_parser::parse_with_edges(path, content),
Language::TypeScript | Language::JavaScript => typescript_parser::parse_with_edges(path, content),
Language::Java => java_parser::parse_with_edges(path, content),
Language::C => c_parser::parse_with_edges(path, content),
Language::Cpp => cpp_parser::parse_with_edges(path, content),
Language::Ruby => ruby_parser::parse_with_edges(path, content),
_ => Ok(ParseResult { nodes: Vec::new(), edges: Vec::new() }),
}
}
#[cfg(test)]
mod tests {
use super::truncate_content;
#[test]
fn test_truncate_ascii_short() {
assert_eq!(truncate_content("hello", 500), "hello");
}
#[test]
fn test_truncate_ascii_exact() {
let s = "a".repeat(500);
assert_eq!(truncate_content(&s, 500), s);
}
#[test]
fn test_truncate_ascii_long() {
let s = "a".repeat(600);
let result = truncate_content(&s, 500);
assert!(result.ends_with("..."));
assert!(result.len() <= 503); }
#[test]
fn test_truncate_multibyte_at_boundary() {
let mut s = "x".repeat(499); s.push('→'); s.push_str("after");
let result = truncate_content(&s, 500);
assert!(result.ends_with("..."));
assert!(!result.contains('→'), "should not include partial char");
assert_eq!(&result[..499], &"x".repeat(499));
}
#[test]
fn test_truncate_emoji_boundary() {
let mut s = "a".repeat(498);
s.push('🦀'); s.push_str("tail");
let result = truncate_content(&s, 500);
assert!(result.ends_with("..."));
assert_eq!(&result[..498], &"a".repeat(498));
}
#[test]
fn test_truncate_all_multibyte() {
let s: String = std::iter::repeat('é').take(300).collect(); let result = truncate_content(&s, 500);
assert!(result.ends_with("..."));
assert_eq!(result.chars().filter(|c| *c == 'é').count(), 250);
}
#[test]
fn test_truncate_empty() {
assert_eq!(truncate_content("", 500), "");
}
#[test]
fn test_truncate_zero_max() {
assert_eq!(truncate_content("hello", 0), "...");
}
}
pub fn parse_auto(path: &Path, content: &str) -> Result<Vec<Node>> {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
let lang = Language::from_extension(ext);
parse_file(path, content, lang)
}