1use crate::code_tools::ToolError;
26use agcodex_ast::Language;
27use dashmap::DashMap;
28use std::collections::HashMap;
29use std::sync::Arc;
30use thiserror::Error;
31use tree_sitter::Query;
32
33#[derive(Debug, Error)]
35pub enum QueryError {
36 #[error("unsupported language: {language}")]
37 UnsupportedLanguage { language: String },
38
39 #[error("invalid query type: {query_type} for language {language}")]
40 InvalidQueryType {
41 query_type: String,
42 language: String,
43 },
44
45 #[error("query compilation failed: {details}")]
46 CompilationFailed { details: String },
47
48 #[error("template not found: {template_name}")]
49 TemplateNotFound { template_name: String },
50
51 #[error("query execution failed: {reason}")]
52 ExecutionFailed { reason: String },
53}
54
55impl From<QueryError> for ToolError {
56 fn from(err: QueryError) -> Self {
57 ToolError::InvalidQuery(err.to_string())
58 }
59}
60
61#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
63pub enum QueryType {
64 Functions,
66 Classes,
68 Imports,
70 Symbols,
72 Methods,
74 Constructors,
76 Signatures,
78 Modules,
80}
81
82#[derive(Debug, Clone)]
84pub struct CompiledQuery {
85 pub query: Arc<Query>,
87 pub language: Language,
89 pub query_type: QueryType,
91 pub description: String,
93 pub capture_names: Vec<String>,
95}
96
97#[derive(Debug, Clone, Hash, PartialEq, Eq)]
99struct CacheKey {
100 language: Language,
101 query_type: QueryType,
102 variant: Option<String>, }
104
105#[derive(Debug)]
107pub struct QueryCache {
108 cache: DashMap<CacheKey, Arc<CompiledQuery>>,
110 stats: DashMap<String, u64>,
112}
113
114impl QueryCache {
115 pub fn new() -> Self {
117 Self {
118 cache: DashMap::new(),
119 stats: DashMap::new(),
120 }
121 }
122
123 pub fn get(
125 &self,
126 language: Language,
127 query_type: QueryType,
128 variant: Option<&str>,
129 ) -> Option<Arc<CompiledQuery>> {
130 let key = CacheKey {
131 language,
132 query_type,
133 variant: variant.map(|s| s.to_string()),
134 };
135
136 if let Some(query) = self.cache.get(&key) {
137 self.increment_stat("cache_hits");
138 Some(query.clone())
139 } else {
140 self.increment_stat("cache_misses");
141 None
142 }
143 }
144
145 pub fn insert(&self, compiled: Arc<CompiledQuery>, variant: Option<&str>) {
147 let key = CacheKey {
148 language: compiled.language,
149 query_type: compiled.query_type,
150 variant: variant.map(|s| s.to_string()),
151 };
152
153 self.cache.insert(key, compiled);
154 self.increment_stat("cache_insertions");
155 }
156
157 pub fn stats(&self) -> HashMap<String, u64> {
159 self.stats
160 .iter()
161 .map(|entry| (entry.key().clone(), *entry.value()))
162 .collect()
163 }
164
165 pub fn clear(&self) {
167 self.cache.clear();
168 self.increment_stat("cache_clears");
169 }
170
171 pub fn size(&self) -> usize {
173 self.cache.len()
174 }
175
176 fn increment_stat(&self, stat_name: &str) {
177 *self.stats.entry(stat_name.to_string()).or_insert(0) += 1;
178 }
179}
180
181impl Default for QueryCache {
182 fn default() -> Self {
183 Self::new()
184 }
185}
186
187#[derive(Debug, Clone)]
189pub struct QueryTemplates {
190 templates: HashMap<(Language, QueryType), &'static str>,
192}
193
194impl QueryTemplates {
195 pub fn new() -> Self {
197 let mut templates = HashMap::new();
198
199 Self::add_rust_templates(&mut templates);
201
202 Self::add_python_templates(&mut templates);
204
205 Self::add_javascript_templates(&mut templates);
207
208 Self::add_go_templates(&mut templates);
210
211 Self::add_java_templates(&mut templates);
213
214 Self::add_c_cpp_templates(&mut templates);
216
217 Self { templates }
218 }
219
220 pub fn get(&self, language: Language, query_type: &QueryType) -> Option<&'static str> {
222 self.templates.get(&(language, *query_type)).copied()
223 }
224
225 pub fn supported_queries(&self, language: Language) -> Vec<QueryType> {
227 self.templates
228 .keys()
229 .filter(|(lang, _)| *lang == language)
230 .map(|(_, query_type)| *query_type)
231 .collect()
232 }
233
234 fn add_rust_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
236 templates.insert(
237 (Language::Rust, QueryType::Functions),
238 r#"
239[
240 (function_item
241 name: (identifier) @name
242 parameters: (parameters) @params
243 body: (block)? @body) @function
244 (associated_item
245 (function_item
246 name: (identifier) @name
247 parameters: (parameters) @params
248 body: (block)? @body) @function)
249]
250"#,
251 );
252
253 templates.insert(
254 (Language::Rust, QueryType::Classes),
255 r#"
256[
257 (struct_item
258 name: (type_identifier) @name
259 body: (field_declaration_list)? @body) @struct
260 (enum_item
261 name: (type_identifier) @name
262 body: (enum_variant_list) @body) @enum
263 (union_item
264 name: (type_identifier) @name
265 body: (field_declaration_list) @body) @union
266 (trait_item
267 name: (type_identifier) @name
268 body: (declaration_list) @body) @trait
269 (impl_item
270 trait: (type_identifier)? @trait_name
271 type: (type_identifier) @type_name
272 body: (declaration_list) @body) @impl
273]
274"#,
275 );
276
277 templates.insert(
278 (Language::Rust, QueryType::Imports),
279 r#"
280[
281 (use_declaration
282 argument: (use_clause) @clause) @import
283 (extern_crate_declaration
284 name: (identifier) @name
285 rename: (identifier)? @rename) @extern_crate
286 (mod_item
287 name: (identifier) @name) @module
288]
289"#,
290 );
291
292 templates.insert(
293 (Language::Rust, QueryType::Symbols),
294 r#"
295[
296 (let_declaration
297 pattern: (identifier) @name
298 type: (_)? @type
299 value: (_)? @value) @variable
300 (const_item
301 name: (identifier) @name
302 type: (_) @type
303 value: (_) @value) @constant
304 (static_item
305 name: (identifier) @name
306 type: (_) @type
307 value: (_) @value) @static
308 (type_item
309 name: (type_identifier) @name
310 type: (_) @type) @type_alias
311]
312"#,
313 );
314
315 templates.insert(
316 (Language::Rust, QueryType::Methods),
317 r#"
318(impl_item
319 body: (declaration_list
320 (function_item
321 name: (identifier) @name
322 parameters: (parameters) @params
323 body: (block)? @body) @method))
324"#,
325 );
326 }
327
328 fn add_python_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
330 templates.insert(
331 (Language::Python, QueryType::Functions),
332 r#"
333[
334 (function_definition
335 name: (identifier) @name
336 parameters: (parameters) @params
337 body: (block) @body) @function
338 (async_function_definition
339 name: (identifier) @name
340 parameters: (parameters) @params
341 body: (block) @body) @async_function
342]
343"#,
344 );
345
346 templates.insert(
347 (Language::Python, QueryType::Classes),
348 r#"
349(class_definition
350 name: (identifier) @name
351 superclasses: (argument_list)? @superclasses
352 body: (block) @body) @class
353"#,
354 );
355
356 templates.insert(
357 (Language::Python, QueryType::Imports),
358 r#"
359[
360 (import_statement
361 name: (dotted_name) @module) @import
362 (import_from_statement
363 module_name: (dotted_name)? @module
364 name: (dotted_name) @name) @import_from
365 (import_from_statement
366 module_name: (dotted_name)? @module
367 name: (aliased_import) @name) @import_from_alias
368]
369"#,
370 );
371
372 templates.insert(
373 (Language::Python, QueryType::Symbols),
374 r#"
375[
376 (assignment
377 left: (identifier) @name
378 right: (_) @value) @variable
379 (assignment
380 left: (pattern_list) @names
381 right: (_) @value) @multiple_assignment
382 (augmented_assignment
383 left: (identifier) @name
384 right: (_) @value) @augmented_variable
385]
386"#,
387 );
388
389 templates.insert(
390 (Language::Python, QueryType::Methods),
391 r#"
392(class_definition
393 body: (block
394 (function_definition
395 name: (identifier) @name
396 parameters: (parameters) @params
397 body: (block) @body) @method))
398"#,
399 );
400 }
401
402 fn add_javascript_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
404 let languages = [Language::JavaScript, Language::TypeScript];
405
406 for &lang in &languages {
407 templates.insert(
408 (lang, QueryType::Functions),
409 r#"
410[
411 (function_declaration
412 name: (identifier) @name
413 parameters: (formal_parameters) @params
414 body: (statement_block) @body) @function
415 (function_expression
416 name: (identifier)? @name
417 parameters: (formal_parameters) @params
418 body: (statement_block) @body) @function_expr
419 (arrow_function
420 parameters: (formal_parameters) @params
421 body: (_) @body) @arrow_function
422 (generator_function_declaration
423 name: (identifier) @name
424 parameters: (formal_parameters) @params
425 body: (statement_block) @body) @generator
426]
427"#,
428 );
429
430 templates.insert(
431 (lang, QueryType::Classes),
432 r#"
433[
434 (class_declaration
435 name: (identifier) @name
436 superclass: (class_heritage)? @superclass
437 body: (class_body) @body) @class
438 (class_expression
439 name: (identifier)? @name
440 superclass: (class_heritage)? @superclass
441 body: (class_body) @body) @class_expr
442]
443"#,
444 );
445
446 templates.insert(
447 (lang, QueryType::Imports),
448 r#"
449[
450 (import_statement
451 source: (string) @source
452 (import_clause
453 (named_imports) @named)?) @import
454 (import_statement
455 source: (string) @source
456 (import_clause
457 (namespace_import) @namespace)?) @import_namespace
458 (export_statement) @export
459]
460"#,
461 );
462
463 templates.insert(
464 (lang, QueryType::Methods),
465 r#"
466(class_body
467 (method_definition
468 name: (property_name) @name
469 parameters: (formal_parameters) @params
470 body: (statement_block) @body) @method)
471"#,
472 );
473
474 templates.insert(
475 (lang, QueryType::Symbols),
476 r#"
477[
478 (variable_declaration
479 (variable_declarator
480 name: (identifier) @name
481 value: (_)? @value)) @variable
482 (lexical_declaration
483 (variable_declarator
484 name: (identifier) @name
485 value: (_)? @value)) @lexical_variable
486]
487"#,
488 );
489 }
490 }
491
492 fn add_go_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
494 templates.insert(
495 (Language::Go, QueryType::Functions),
496 r#"
497[
498 (function_declaration
499 name: (identifier) @name
500 parameters: (parameter_list) @params
501 result: (_)? @return_type
502 body: (block) @body) @function
503 (method_declaration
504 receiver: (parameter_list) @receiver
505 name: (identifier) @name
506 parameters: (parameter_list) @params
507 result: (_)? @return_type
508 body: (block) @body) @method
509]
510"#,
511 );
512
513 templates.insert(
514 (Language::Go, QueryType::Classes),
515 r#"
516[
517 (type_declaration
518 (type_spec
519 name: (type_identifier) @name
520 type: (struct_type) @struct_body)) @struct
521 (type_declaration
522 (type_spec
523 name: (type_identifier) @name
524 type: (interface_type) @interface_body)) @interface
525]
526"#,
527 );
528
529 templates.insert(
530 (Language::Go, QueryType::Imports),
531 r#"
532[
533 (import_declaration
534 (import_spec
535 name: (package_identifier)? @alias
536 path: (interpreted_string_literal) @path)) @import
537 (package_clause
538 (package_identifier) @package_name) @package
539]
540"#,
541 );
542
543 templates.insert(
544 (Language::Go, QueryType::Symbols),
545 r#"
546[
547 (var_declaration
548 (var_spec
549 name: (identifier) @name
550 type: (_)? @type
551 value: (_)? @value)) @variable
552 (const_declaration
553 (const_spec
554 name: (identifier) @name
555 type: (_)? @type
556 value: (_) @value)) @constant
557]
558"#,
559 );
560 }
561
562 fn add_java_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
564 templates.insert(
565 (Language::Java, QueryType::Functions),
566 r#"
567(method_declaration
568 (modifiers)? @modifiers
569 type: (_) @return_type
570 name: (identifier) @name
571 parameters: (formal_parameters) @params
572 body: (block)? @body) @method
573"#,
574 );
575
576 templates.insert(
577 (Language::Java, QueryType::Classes),
578 r#"
579[
580 (class_declaration
581 (modifiers)? @modifiers
582 name: (identifier) @name
583 superclass: (superclass)? @superclass
584 interfaces: (super_interfaces)? @interfaces
585 body: (class_body) @body) @class
586 (interface_declaration
587 (modifiers)? @modifiers
588 name: (identifier) @name
589 extends: (extends_interfaces)? @extends
590 body: (interface_body) @body) @interface
591 (enum_declaration
592 (modifiers)? @modifiers
593 name: (identifier) @name
594 interfaces: (super_interfaces)? @interfaces
595 body: (enum_body) @body) @enum
596]
597"#,
598 );
599
600 templates.insert(
601 (Language::Java, QueryType::Imports),
602 r#"
603[
604 (import_declaration
605 (scoped_identifier) @import_path) @import
606 (import_declaration
607 (asterisk) @wildcard) @wildcard_import
608 (package_declaration
609 (scoped_identifier) @package_name) @package
610]
611"#,
612 );
613
614 templates.insert(
615 (Language::Java, QueryType::Symbols),
616 r#"
617[
618 (field_declaration
619 (modifiers)? @modifiers
620 type: (_) @type
621 (variable_declarator
622 name: (identifier) @name
623 value: (_)? @value)) @field
624 (local_variable_declaration
625 type: (_) @type
626 (variable_declarator
627 name: (identifier) @name
628 value: (_)? @value)) @local_variable
629]
630"#,
631 );
632
633 templates.insert(
634 (Language::Java, QueryType::Constructors),
635 r#"
636(constructor_declaration
637 (modifiers)? @modifiers
638 name: (identifier) @name
639 parameters: (formal_parameters) @params
640 body: (constructor_body) @body) @constructor
641"#,
642 );
643 }
644
645 fn add_c_cpp_templates(templates: &mut HashMap<(Language, QueryType), &'static str>) {
647 let languages = [Language::C, Language::Cpp];
648
649 for &lang in &languages {
650 templates.insert(
651 (lang, QueryType::Functions),
652 r#"
653[
654 (function_definition
655 type: (_) @return_type
656 declarator: (function_declarator
657 declarator: (_) @name
658 parameters: (parameter_list) @params)
659 body: (compound_statement) @body) @function
660 (declaration
661 type: (_) @return_type
662 declarator: (function_declarator
663 declarator: (_) @name
664 parameters: (parameter_list) @params)) @function_declaration
665]
666"#,
667 );
668
669 templates.insert(
670 (lang, QueryType::Classes),
671 r#"
672[
673 (struct_specifier
674 name: (type_identifier) @name
675 body: (field_declaration_list) @body) @struct
676 (union_specifier
677 name: (type_identifier) @name
678 body: (field_declaration_list) @body) @union
679 (enum_specifier
680 name: (type_identifier) @name
681 body: (enumerator_list) @body) @enum
682]
683"#,
684 );
685
686 templates.insert(
687 (lang, QueryType::Imports),
688 r#"
689[
690 (preproc_include
691 path: (string_literal) @path) @include
692 (preproc_include
693 path: (system_lib_string) @path) @include_system
694]
695"#,
696 );
697
698 templates.insert(
699 (lang, QueryType::Symbols),
700 r#"
701[
702 (declaration
703 type: (_) @type
704 declarator: (identifier) @name) @variable
705 (init_declarator
706 declarator: (identifier) @name
707 value: (_) @value) @initialized_variable
708 (preproc_def
709 name: (identifier) @name
710 value: (_)? @value) @macro
711]
712"#,
713 );
714 }
715
716 templates.insert(
718 (Language::Cpp, QueryType::Classes),
719 r#"
720[
721 (class_specifier
722 name: (type_identifier) @name
723 base: (base_class_clause)? @base_classes
724 body: (field_declaration_list) @body) @class
725 (struct_specifier
726 name: (type_identifier) @name
727 base: (base_class_clause)? @base_classes
728 body: (field_declaration_list) @body) @struct
729 (union_specifier
730 name: (type_identifier) @name
731 body: (field_declaration_list) @body) @union
732 (enum_specifier
733 name: (type_identifier) @name
734 body: (enumerator_list) @body) @enum
735]
736"#,
737 );
738
739 templates.insert(
740 (Language::Cpp, QueryType::Imports),
741 r#"
742[
743 (preproc_include
744 path: (string_literal) @path) @include
745 (preproc_include
746 path: (system_lib_string) @path) @include_system
747 (using_declaration
748 (qualified_identifier) @name) @using
749 (namespace_definition
750 name: (identifier) @name
751 body: (declaration_list) @body) @namespace
752]
753"#,
754 );
755 }
756}
757
758impl Default for QueryTemplates {
759 fn default() -> Self {
760 Self::new()
761 }
762}
763
764#[derive(Debug, Clone)]
766pub struct QueryBuilder {
767 templates: Arc<QueryTemplates>,
769 cache: Arc<QueryCache>,
771}
772
773impl QueryBuilder {
774 pub fn new() -> Self {
776 Self {
777 templates: Arc::new(QueryTemplates::new()),
778 cache: Arc::new(QueryCache::new()),
779 }
780 }
781
782 pub fn with_cache(cache: Arc<QueryCache>) -> Self {
784 Self {
785 templates: Arc::new(QueryTemplates::new()),
786 cache,
787 }
788 }
789
790 pub fn build_query(
792 &self,
793 language: Language,
794 query_type: QueryType,
795 custom_template: Option<&str>,
796 ) -> Result<Arc<CompiledQuery>, QueryError> {
797 if let Some(cached) = self.cache.get(language, query_type, None) {
799 return Ok(cached);
800 }
801
802 let template = if let Some(custom) = custom_template {
804 custom
805 } else {
806 self.templates.get(language, &query_type).ok_or_else(|| {
807 QueryError::InvalidQueryType {
808 query_type: format!("{:?}", query_type),
809 language: language.name().to_string(),
810 }
811 })?
812 };
813
814 let ts_language = language.parser();
816 let query =
817 Query::new(&ts_language, template).map_err(|e| QueryError::CompilationFailed {
818 details: format!("Tree-sitter compilation error: {}", e),
819 })?;
820
821 let capture_names = query
823 .capture_names()
824 .iter()
825 .map(|&s| s.to_string())
826 .collect();
827
828 let compiled = Arc::new(CompiledQuery {
830 query: Arc::new(query),
831 language,
832 query_type,
833 description: format!("{:?} queries for {}", query_type, language.name()),
834 capture_names,
835 });
836
837 self.cache.insert(compiled.clone(), None);
839
840 Ok(compiled)
841 }
842
843 pub fn build_custom_query(
845 &self,
846 language: Language,
847 query_type: QueryType,
848 template: &str,
849 variant: &str,
850 ) -> Result<Arc<CompiledQuery>, QueryError> {
851 if let Some(cached) = self.cache.get(language, query_type, Some(variant)) {
853 return Ok(cached);
854 }
855
856 let ts_language = language.parser();
858 let query =
859 Query::new(&ts_language, template).map_err(|e| QueryError::CompilationFailed {
860 details: format!("Custom query compilation error: {}", e),
861 })?;
862
863 let capture_names = query
865 .capture_names()
866 .iter()
867 .map(|&s| s.to_string())
868 .collect();
869
870 let compiled = Arc::new(CompiledQuery {
872 query: Arc::new(query),
873 language,
874 query_type,
875 description: format!(
876 "Custom {:?} query ({}) for {}",
877 query_type,
878 variant,
879 language.name()
880 ),
881 capture_names,
882 });
883
884 self.cache.insert(compiled.clone(), Some(variant));
886
887 Ok(compiled)
888 }
889
890 pub fn supported_queries(&self, language: Language) -> Vec<QueryType> {
892 self.templates.supported_queries(language)
893 }
894
895 pub fn cache_stats(&self) -> HashMap<String, u64> {
897 self.cache.stats()
898 }
899
900 pub fn clear_cache(&self) {
902 self.cache.clear();
903 }
904}
905
906impl Default for QueryBuilder {
907 fn default() -> Self {
908 Self::new()
909 }
910}
911
912#[derive(Debug, Clone)]
914pub struct QueryLibrary {
915 builder: QueryBuilder,
917 cache: Arc<QueryCache>,
919 templates: Arc<QueryTemplates>,
921}
922
923impl QueryLibrary {
924 pub fn new() -> Self {
926 let cache = Arc::new(QueryCache::new());
927 Self {
928 builder: QueryBuilder::with_cache(cache.clone()),
929 cache,
930 templates: Arc::new(QueryTemplates::new()),
931 }
932 }
933
934 pub fn get_query(
936 &self,
937 language: Language,
938 query_type: QueryType,
939 ) -> Result<Arc<CompiledQuery>, QueryError> {
940 self.builder.build_query(language, query_type, None)
941 }
942
943 pub fn get_custom_query(
945 &self,
946 language: Language,
947 query_type: QueryType,
948 template: &str,
949 variant: &str,
950 ) -> Result<Arc<CompiledQuery>, QueryError> {
951 self.builder
952 .build_custom_query(language, query_type, template, variant)
953 }
954
955 pub fn supports_query(&self, language: Language, query_type: &QueryType) -> bool {
957 self.templates.get(language, query_type).is_some()
958 }
959
960 pub fn supported_languages(&self) -> Vec<Language> {
962 vec![
964 Language::Rust,
965 Language::Python,
966 Language::JavaScript,
967 Language::TypeScript,
968 Language::Go,
969 Language::Java,
970 Language::C,
971 Language::Cpp,
972 ]
973 }
974
975 pub fn supported_query_types(&self, language: Language) -> Vec<QueryType> {
977 self.builder.supported_queries(language)
978 }
979
980 pub fn stats(&self) -> QueryLibraryStats {
982 let cache_stats = self.cache.stats();
983 QueryLibraryStats {
984 cache_size: self.cache.size(),
985 cache_hits: cache_stats.get("cache_hits").copied().unwrap_or(0),
986 cache_misses: cache_stats.get("cache_misses").copied().unwrap_or(0),
987 total_queries: cache_stats.get("cache_insertions").copied().unwrap_or(0),
988 supported_languages: self.supported_languages().len(),
989 }
990 }
991
992 pub fn clear_cache(&self) {
994 self.cache.clear();
995 }
996
997 pub fn precompile_language(&self, language: Language) -> Result<usize, QueryError> {
999 let query_types = self.supported_query_types(language);
1000 let mut compiled_count = 0;
1001
1002 for query_type in query_types {
1003 match self.get_query(language, query_type) {
1004 Ok(_) => compiled_count += 1,
1005 Err(e) => {
1006 eprintln!(
1008 "Warning: Failed to precompile {:?} for {}: {}",
1009 query_type,
1010 language.name(),
1011 e
1012 );
1013 }
1014 }
1015 }
1016
1017 Ok(compiled_count)
1018 }
1019
1020 pub fn precompile_all(&self) -> Result<usize, QueryError> {
1022 let languages = self.supported_languages();
1023 let mut total_compiled = 0;
1024
1025 for language in languages {
1026 match self.precompile_language(language) {
1027 Ok(count) => total_compiled += count,
1028 Err(e) => {
1029 eprintln!(
1030 "Warning: Failed to precompile queries for {}: {}",
1031 language.name(),
1032 e
1033 );
1034 }
1035 }
1036 }
1037
1038 Ok(total_compiled)
1039 }
1040}
1041
1042impl Default for QueryLibrary {
1043 fn default() -> Self {
1044 Self::new()
1045 }
1046}
1047
1048#[derive(Debug, Clone)]
1050pub struct QueryLibraryStats {
1051 pub cache_size: usize,
1053 pub cache_hits: u64,
1055 pub cache_misses: u64,
1057 pub total_queries: u64,
1059 pub supported_languages: usize,
1061}
1062
1063impl QueryLibraryStats {
1064 pub fn hit_rate(&self) -> f64 {
1066 let total_requests = self.cache_hits + self.cache_misses;
1067 if total_requests == 0 {
1068 0.0
1069 } else {
1070 self.cache_hits as f64 / total_requests as f64
1071 }
1072 }
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078
1079 #[test]
1080 fn test_query_cache_basic_operations() {
1081 let cache = QueryCache::new();
1082 assert_eq!(cache.size(), 0);
1083
1084 assert!(
1086 cache
1087 .get(Language::Rust, QueryType::Functions, None)
1088 .is_none()
1089 );
1090
1091 let stats = cache.stats();
1093 assert_eq!(stats.get("cache_misses"), Some(&1));
1094 }
1095
1096 #[test]
1097 fn test_query_templates_rust() {
1098 let templates = QueryTemplates::new();
1099
1100 assert!(
1102 templates
1103 .get(Language::Rust, &QueryType::Functions)
1104 .is_some()
1105 );
1106 assert!(templates.get(Language::Rust, &QueryType::Classes).is_some());
1107 assert!(templates.get(Language::Rust, &QueryType::Imports).is_some());
1108
1109 let supported = templates.supported_queries(Language::Rust);
1111 assert!(supported.contains(&QueryType::Functions));
1112 assert!(supported.contains(&QueryType::Classes));
1113 assert!(supported.contains(&QueryType::Imports));
1114 }
1115
1116 #[test]
1117 fn test_query_library_creation() {
1118 let library = QueryLibrary::new();
1119
1120 let languages = library.supported_languages();
1122 assert!(languages.contains(&Language::Rust));
1123 assert!(languages.contains(&Language::Python));
1124 assert!(languages.contains(&Language::JavaScript));
1125
1126 let stats = library.stats();
1128 assert_eq!(stats.cache_size, 0);
1129 assert_eq!(stats.cache_hits, 0);
1130 assert_eq!(stats.cache_misses, 0);
1131 }
1132
1133 #[test]
1134 fn test_query_library_support_check() {
1135 let library = QueryLibrary::new();
1136
1137 assert!(library.supports_query(Language::Rust, &QueryType::Functions));
1139 assert!(library.supports_query(Language::Python, &QueryType::Classes));
1140 assert!(library.supports_query(Language::JavaScript, &QueryType::Imports));
1141
1142 let rust_queries = library.supported_query_types(Language::Rust);
1144 assert!(!rust_queries.is_empty());
1145 assert!(rust_queries.contains(&QueryType::Functions));
1146 }
1147
1148 #[test]
1149 fn test_stats_hit_rate_calculation() {
1150 let stats = QueryLibraryStats {
1151 cache_size: 10,
1152 cache_hits: 8,
1153 cache_misses: 2,
1154 total_queries: 10,
1155 supported_languages: 5,
1156 };
1157
1158 assert_eq!(stats.hit_rate(), 0.8);
1159
1160 let empty_stats = QueryLibraryStats {
1161 cache_size: 0,
1162 cache_hits: 0,
1163 cache_misses: 0,
1164 total_queries: 0,
1165 supported_languages: 0,
1166 };
1167
1168 assert_eq!(empty_stats.hit_rate(), 0.0);
1169 }
1170}