1pub mod rust_parser;
11pub mod python_parser;
12pub mod go_parser;
13pub mod typescript_parser;
14pub mod java_parser;
15pub mod c_parser;
16pub mod cpp_parser;
17
18#[cfg(feature = "pattern")]
19pub mod pattern;
20
21#[cfg(feature = "text-search")]
22pub mod text_search;
23
24use deagle_core::{Language, Node, Result};
25use std::path::Path;
26
27pub use rust_parser::ParseResult;
28
29pub(crate) fn truncate_content(s: &str, max_bytes: usize) -> String {
31 if s.len() <= max_bytes {
32 return s.to_string();
33 }
34 let mut end = max_bytes;
36 while end > 0 && !s.is_char_boundary(end) {
37 end -= 1;
38 }
39 format!("{}...", &s[..end])
40}
41
42pub fn parse_file(path: &Path, content: &str, language: Language) -> Result<Vec<Node>> {
44 match language {
45 Language::Rust => rust_parser::parse(path, content),
46 Language::Python => python_parser::parse(path, content),
47 Language::Go => go_parser::parse(path, content),
48 Language::TypeScript | Language::JavaScript => typescript_parser::parse(path, content),
49 Language::Java => java_parser::parse(path, content),
50 Language::C => c_parser::parse(path, content),
51 Language::Cpp => cpp_parser::parse(path, content),
52 _ => Ok(Vec::new()),
53 }
54}
55
56pub fn parse_file_with_edges(path: &Path, content: &str, language: Language) -> Result<ParseResult> {
58 match language {
59 Language::Rust => rust_parser::parse_with_edges(path, content),
60 Language::Python => python_parser::parse_with_edges(path, content),
61 Language::Go => go_parser::parse_with_edges(path, content),
62 Language::TypeScript | Language::JavaScript => typescript_parser::parse_with_edges(path, content),
63 Language::Java => java_parser::parse_with_edges(path, content),
64 Language::C => c_parser::parse_with_edges(path, content),
65 Language::Cpp => cpp_parser::parse_with_edges(path, content),
66 _ => Ok(ParseResult { nodes: Vec::new(), edges: Vec::new() }),
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::truncate_content;
73
74 #[test]
75 fn test_truncate_ascii_short() {
76 assert_eq!(truncate_content("hello", 500), "hello");
77 }
78
79 #[test]
80 fn test_truncate_ascii_exact() {
81 let s = "a".repeat(500);
82 assert_eq!(truncate_content(&s, 500), s);
83 }
84
85 #[test]
86 fn test_truncate_ascii_long() {
87 let s = "a".repeat(600);
88 let result = truncate_content(&s, 500);
89 assert!(result.ends_with("..."));
90 assert!(result.len() <= 503); }
92
93 #[test]
94 fn test_truncate_multibyte_at_boundary() {
95 let mut s = "x".repeat(499); s.push('→'); s.push_str("after");
99 let result = truncate_content(&s, 500);
101 assert!(result.ends_with("..."));
102 assert!(!result.contains('→'), "should not include partial char");
103 assert_eq!(&result[..499], &"x".repeat(499));
104 }
105
106 #[test]
107 fn test_truncate_emoji_boundary() {
108 let mut s = "a".repeat(498);
110 s.push('🦀'); s.push_str("tail");
112 let result = truncate_content(&s, 500);
113 assert!(result.ends_with("..."));
114 assert_eq!(&result[..498], &"a".repeat(498));
116 }
117
118 #[test]
119 fn test_truncate_all_multibyte() {
120 let s: String = std::iter::repeat('é').take(300).collect(); let result = truncate_content(&s, 500);
123 assert!(result.ends_with("..."));
124 assert_eq!(result.chars().filter(|c| *c == 'é').count(), 250);
126 }
127
128 #[test]
129 fn test_truncate_empty() {
130 assert_eq!(truncate_content("", 500), "");
131 }
132
133 #[test]
134 fn test_truncate_zero_max() {
135 assert_eq!(truncate_content("hello", 0), "...");
136 }
137}
138
139pub fn parse_auto(path: &Path, content: &str) -> Result<Vec<Node>> {
141 let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
142 let lang = Language::from_extension(ext);
143 parse_file(path, content, lang)
144}