Skip to main content

infigraph_languages/
lib.rs

1use anyhow::Result;
2use infigraph_core::lang::{CustomEdgeDef, LanguagePack, LanguageRegistry};
3
4const PYTHON_ENTITIES: &str = include_str!("../languages/python/entities.scm");
5const PYTHON_RELATIONS: &str = include_str!("../languages/python/relations.scm");
6
7const RUST_ENTITIES: &str = include_str!("../languages/rust/entities.scm");
8const RUST_RELATIONS: &str = include_str!("../languages/rust/relations.scm");
9
10const TYPESCRIPT_ENTITIES: &str = include_str!("../languages/typescript/entities.scm");
11const TYPESCRIPT_RELATIONS: &str = include_str!("../languages/typescript/relations.scm");
12
13const JAVASCRIPT_ENTITIES: &str = include_str!("../languages/javascript/entities.scm");
14const JAVASCRIPT_RELATIONS: &str = include_str!("../languages/javascript/relations.scm");
15
16const GO_ENTITIES: &str = include_str!("../languages/go/entities.scm");
17const GO_RELATIONS: &str = include_str!("../languages/go/relations.scm");
18
19const JAVA_ENTITIES: &str = include_str!("../languages/java/entities.scm");
20const JAVA_RELATIONS: &str = include_str!("../languages/java/relations.scm");
21
22const C_ENTITIES: &str = include_str!("../languages/c/entities.scm");
23const C_RELATIONS: &str = include_str!("../languages/c/relations.scm");
24
25const CPP_ENTITIES: &str = include_str!("../languages/cpp/entities.scm");
26const CPP_RELATIONS: &str = include_str!("../languages/cpp/relations.scm");
27
28const RUBY_ENTITIES: &str = include_str!("../languages/ruby/entities.scm");
29const RUBY_RELATIONS: &str = include_str!("../languages/ruby/relations.scm");
30
31const PHP_ENTITIES: &str = include_str!("../languages/php/entities.scm");
32const PHP_RELATIONS: &str = include_str!("../languages/php/relations.scm");
33
34const SWIFT_ENTITIES: &str = include_str!("../languages/swift/entities.scm");
35const SWIFT_RELATIONS: &str = include_str!("../languages/swift/relations.scm");
36
37const KOTLIN_ENTITIES: &str = include_str!("../languages/kotlin/entities.scm");
38const KOTLIN_RELATIONS: &str = include_str!("../languages/kotlin/relations.scm");
39
40const CSHARP_ENTITIES: &str = include_str!("../languages/csharp/entities.scm");
41const CSHARP_RELATIONS: &str = include_str!("../languages/csharp/relations.scm");
42
43const SCALA_ENTITIES: &str = include_str!("../languages/scala/entities.scm");
44const SCALA_RELATIONS: &str = include_str!("../languages/scala/relations.scm");
45
46const LUA_ENTITIES: &str = include_str!("../languages/lua/entities.scm");
47const LUA_RELATIONS: &str = include_str!("../languages/lua/relations.scm");
48
49const ZIG_ENTITIES: &str = include_str!("../languages/zig/entities.scm");
50const ZIG_RELATIONS: &str = include_str!("../languages/zig/relations.scm");
51
52const ELIXIR_ENTITIES: &str = include_str!("../languages/elixir/entities.scm");
53const ELIXIR_RELATIONS: &str = include_str!("../languages/elixir/relations.scm");
54
55const DART_ENTITIES: &str = include_str!("../languages/dart/entities.scm");
56const DART_RELATIONS: &str = include_str!("../languages/dart/relations.scm");
57
58const OBJC_ENTITIES: &str = include_str!("../languages/objc/entities.scm");
59const OBJC_RELATIONS: &str = include_str!("../languages/objc/relations.scm");
60
61const HASKELL_ENTITIES: &str = include_str!("../languages/haskell/entities.scm");
62const HASKELL_RELATIONS: &str = include_str!("../languages/haskell/relations.scm");
63
64const PERL_ENTITIES: &str = include_str!("../languages/perl/entities.scm");
65const PERL_RELATIONS: &str = include_str!("../languages/perl/relations.scm");
66
67const R_ENTITIES: &str = include_str!("../languages/r/entities.scm");
68const R_RELATIONS: &str = include_str!("../languages/r/relations.scm");
69
70const OCAML_ENTITIES: &str = include_str!("../languages/ocaml/entities.scm");
71const OCAML_RELATIONS: &str = include_str!("../languages/ocaml/relations.scm");
72
73const BASH_ENTITIES: &str = include_str!("../languages/bash/entities.scm");
74const BASH_RELATIONS: &str = include_str!("../languages/bash/relations.scm");
75
76const SQL_ENTITIES: &str = include_str!("../languages/sql/entities.scm");
77const SQL_RELATIONS: &str = include_str!("../languages/sql/relations.scm");
78
79const JULIA_ENTITIES: &str = include_str!("../languages/julia/entities.scm");
80const JULIA_RELATIONS: &str = include_str!("../languages/julia/relations.scm");
81
82const PROTO_ENTITIES: &str = include_str!("../languages/proto/entities.scm");
83const PROTO_RELATIONS: &str = include_str!("../languages/proto/relations.scm");
84
85const POWERSHELL_ENTITIES: &str = include_str!("../languages/powershell/entities.scm");
86const POWERSHELL_RELATIONS: &str = include_str!("../languages/powershell/relations.scm");
87
88const VERILOG_ENTITIES: &str = include_str!("../languages/verilog/entities.scm");
89const VERILOG_RELATIONS: &str = include_str!("../languages/verilog/relations.scm");
90
91const HCL_ENTITIES: &str = include_str!("../languages/hcl/entities.scm");
92const HCL_RELATIONS: &str = include_str!("../languages/hcl/relations.scm");
93
94const TOML_ENTITIES: &str = include_str!("../languages/toml/entities.scm");
95const TOML_RELATIONS: &str = include_str!("../languages/toml/relations.scm");
96
97const YAML_ENTITIES: &str = include_str!("../languages/yaml/entities.scm");
98const YAML_RELATIONS: &str = include_str!("../languages/yaml/relations.scm");
99
100const ERLANG_ENTITIES: &str = include_str!("../languages/erlang/entities.scm");
101const ERLANG_RELATIONS: &str = include_str!("../languages/erlang/relations.scm");
102
103const DOCKERFILE_ENTITIES: &str = include_str!("../languages/dockerfile/entities.scm");
104const DOCKERFILE_RELATIONS: &str = include_str!("../languages/dockerfile/relations.scm");
105
106const FORTRAN_ENTITIES: &str = include_str!("../languages/fortran/entities.scm");
107const FORTRAN_RELATIONS: &str = include_str!("../languages/fortran/relations.scm");
108
109const NIX_ENTITIES: &str = include_str!("../languages/nix/entities.scm");
110const NIX_RELATIONS: &str = include_str!("../languages/nix/relations.scm");
111
112const SVELTE_ENTITIES: &str = include_str!("../languages/svelte/entities.scm");
113const SVELTE_RELATIONS: &str = include_str!("../languages/svelte/relations.scm");
114
115const FSHARP_ENTITIES: &str = include_str!("../languages/fsharp/entities.scm");
116const FSHARP_RELATIONS: &str = include_str!("../languages/fsharp/relations.scm");
117
118const GROOVY_ENTITIES: &str = include_str!("../languages/groovy/entities.scm");
119const GROOVY_RELATIONS: &str = include_str!("../languages/groovy/relations.scm");
120
121const CSS_ENTITIES: &str = include_str!("../languages/css/entities.scm");
122const CSS_RELATIONS: &str = include_str!("../languages/css/relations.scm");
123
124const HTML_ENTITIES: &str = include_str!("../languages/html/entities.scm");
125const HTML_RELATIONS: &str = include_str!("../languages/html/relations.scm");
126
127const JSON_ENTITIES: &str = include_str!("../languages/json/entities.scm");
128const JSON_RELATIONS: &str = include_str!("../languages/json/relations.scm");
129
130const XML_ENTITIES: &str = include_str!("../languages/xml/entities.scm");
131const XML_RELATIONS: &str = include_str!("../languages/xml/relations.scm");
132
133const MAKEFILE_ENTITIES: &str = include_str!("../languages/makefile/entities.scm");
134const MAKEFILE_RELATIONS: &str = include_str!("../languages/makefile/relations.scm");
135
136const CMAKE_ENTITIES: &str = include_str!("../languages/cmake/entities.scm");
137const CMAKE_RELATIONS: &str = include_str!("../languages/cmake/relations.scm");
138
139const GRAPHQL_ENTITIES: &str = include_str!("../languages/graphql/entities.scm");
140const GRAPHQL_RELATIONS: &str = include_str!("../languages/graphql/relations.scm");
141
142const GLSL_ENTITIES: &str = include_str!("../languages/glsl/entities.scm");
143const GLSL_RELATIONS: &str = include_str!("../languages/glsl/relations.scm");
144
145const COMMONLISP_ENTITIES: &str = include_str!("../languages/commonlisp/entities.scm");
146const COMMONLISP_RELATIONS: &str = include_str!("../languages/commonlisp/relations.scm");
147
148const ELM_ENTITIES: &str = include_str!("../languages/elm/entities.scm");
149const ELM_RELATIONS: &str = include_str!("../languages/elm/relations.scm");
150
151const ELISP_ENTITIES: &str = include_str!("../languages/elisp/entities.scm");
152const ELISP_RELATIONS: &str = include_str!("../languages/elisp/relations.scm");
153
154const INI_ENTITIES: &str = include_str!("../languages/ini/entities.scm");
155const INI_RELATIONS: &str = include_str!("../languages/ini/relations.scm");
156
157const TSX_ENTITIES: &str = include_str!("../languages/tsx/entities.scm");
158const TSX_RELATIONS: &str = include_str!("../languages/tsx/relations.scm");
159
160const STARLARK_ENTITIES: &str = include_str!("../languages/starlark/entities.scm");
161const STARLARK_RELATIONS: &str = include_str!("../languages/starlark/relations.scm");
162
163const MATLAB_ENTITIES: &str = include_str!("../languages/matlab/entities.scm");
164const MATLAB_RELATIONS: &str = include_str!("../languages/matlab/relations.scm");
165
166const MARKDOWN_ENTITIES: &str = include_str!("../languages/markdown/entities.scm");
167const MARKDOWN_RELATIONS: &str = include_str!("../languages/markdown/relations.scm");
168
169const CLOJURE_ENTITIES: &str = include_str!("../languages/clojure/entities.scm");
170const CLOJURE_RELATIONS: &str = include_str!("../languages/clojure/relations.scm");
171
172const CUDA_ENTITIES: &str = include_str!("../languages/cuda/entities.scm");
173const CUDA_RELATIONS: &str = include_str!("../languages/cuda/relations.scm");
174
175const PASCAL_ENTITIES: &str = include_str!("../languages/pascal/entities.scm");
176const PASCAL_RELATIONS: &str = include_str!("../languages/pascal/relations.scm");
177
178const VB6_ENTITIES: &str = include_str!("../languages/vb6/entities.scm");
179const VB6_RELATIONS: &str = include_str!("../languages/vb6/relations.scm");
180
181/// Create a registry with all bundled language packs.
182pub fn bundled_registry() -> Result<LanguageRegistry> {
183    let mut registry = LanguageRegistry::new();
184
185    registry.register(python_pack()?);
186
187    // These may fail if queries don't match the grammar — log and skip
188    match rust_pack() {
189        Ok(pack) => registry.register(pack),
190        Err(e) => eprintln!("warning: failed to load Rust language pack: {e}"),
191    }
192    match typescript_pack() {
193        Ok(pack) => registry.register(pack),
194        Err(e) => eprintln!("warning: failed to load TypeScript language pack: {e}"),
195    }
196    if let Ok(pack) = javascript_pack() {
197        registry.register(pack);
198    } else {
199        eprintln!("warning: failed to load JavaScript language pack");
200    }
201    if let Ok(pack) = go_pack() {
202        registry.register(pack);
203    } else {
204        eprintln!("warning: failed to load Go language pack");
205    }
206    if let Ok(pack) = java_pack() {
207        registry.register(pack);
208    } else {
209        eprintln!("warning: failed to load Java language pack");
210    }
211    match c_pack() {
212        Ok(pack) => registry.register(pack),
213        Err(e) => eprintln!("warning: failed to load C language pack: {e}"),
214    }
215    match cpp_pack() {
216        Ok(pack) => registry.register(pack),
217        Err(e) => eprintln!("warning: failed to load C++ language pack: {e}"),
218    }
219    match ruby_pack() {
220        Ok(pack) => registry.register(pack),
221        Err(e) => eprintln!("warning: failed to load Ruby language pack: {e}"),
222    }
223    match php_pack() {
224        Ok(pack) => registry.register(pack),
225        Err(e) => eprintln!("warning: failed to load PHP language pack: {e}"),
226    }
227    match swift_pack() {
228        Ok(pack) => registry.register(pack),
229        Err(e) => eprintln!("warning: failed to load Swift language pack: {e}"),
230    }
231    match kotlin_pack() {
232        Ok(pack) => registry.register(pack),
233        Err(e) => eprintln!("warning: failed to load Kotlin language pack: {e}"),
234    }
235    match csharp_pack() {
236        Ok(pack) => registry.register(pack),
237        Err(e) => eprintln!("warning: failed to load C# language pack: {e}"),
238    }
239    match scala_pack() {
240        Ok(pack) => registry.register(pack),
241        Err(e) => eprintln!("warning: failed to load Scala language pack: {e}"),
242    }
243    match lua_pack() {
244        Ok(pack) => registry.register(pack),
245        Err(e) => eprintln!("warning: failed to load Lua language pack: {e}"),
246    }
247    match zig_pack() {
248        Ok(pack) => registry.register(pack),
249        Err(e) => eprintln!("warning: failed to load Zig language pack: {e}"),
250    }
251    match elixir_pack() {
252        Ok(pack) => registry.register(pack),
253        Err(e) => eprintln!("warning: failed to load Elixir language pack: {e}"),
254    }
255    match dart_pack() {
256        Ok(pack) => registry.register(pack),
257        Err(e) => eprintln!("warning: failed to load Dart language pack: {e}"),
258    }
259    match objc_pack() {
260        Ok(pack) => registry.register(pack),
261        Err(e) => eprintln!("warning: failed to load Objective-C language pack: {e}"),
262    }
263    match haskell_pack() {
264        Ok(pack) => registry.register(pack),
265        Err(e) => eprintln!("warning: failed to load Haskell language pack: {e}"),
266    }
267    match perl_pack() {
268        Ok(pack) => registry.register(pack),
269        Err(e) => eprintln!("warning: failed to load Perl language pack: {e}"),
270    }
271    match r_pack() {
272        Ok(pack) => registry.register(pack),
273        Err(e) => eprintln!("warning: failed to load R language pack: {e}"),
274    }
275    match ocaml_pack() {
276        Ok(pack) => registry.register(pack),
277        Err(e) => eprintln!("warning: failed to load OCaml language pack: {e}"),
278    }
279    match bash_pack() {
280        Ok(pack) => registry.register(pack),
281        Err(e) => eprintln!("warning: failed to load Bash language pack: {e}"),
282    }
283    match sql_pack() {
284        Ok(pack) => registry.register(pack),
285        Err(e) => eprintln!("warning: failed to load SQL language pack: {e}"),
286    }
287    match julia_pack() {
288        Ok(pack) => registry.register(pack),
289        Err(e) => eprintln!("warning: failed to load Julia language pack: {e}"),
290    }
291    match proto_pack() {
292        Ok(pack) => registry.register(pack),
293        Err(e) => eprintln!("warning: failed to load Protobuf language pack: {e}"),
294    }
295    match powershell_pack() {
296        Ok(pack) => registry.register(pack),
297        Err(e) => eprintln!("warning: failed to load PowerShell language pack: {e}"),
298    }
299    match verilog_pack() {
300        Ok(pack) => registry.register(pack),
301        Err(e) => eprintln!("warning: failed to load Verilog language pack: {e}"),
302    }
303    match hcl_pack() {
304        Ok(pack) => registry.register(pack),
305        Err(e) => eprintln!("warning: failed to load HCL language pack: {e}"),
306    }
307    match toml_pack() {
308        Ok(pack) => registry.register(pack),
309        Err(e) => eprintln!("warning: failed to load TOML language pack: {e}"),
310    }
311    match yaml_pack() {
312        Ok(pack) => registry.register(pack),
313        Err(e) => eprintln!("warning: failed to load YAML language pack: {e}"),
314    }
315    match erlang_pack() {
316        Ok(pack) => registry.register(pack),
317        Err(e) => eprintln!("warning: failed to load Erlang language pack: {e}"),
318    }
319    match dockerfile_pack() {
320        Ok(pack) => registry.register(pack),
321        Err(e) => eprintln!("warning: failed to load Dockerfile language pack: {e}"),
322    }
323    match fortran_pack() {
324        Ok(pack) => registry.register(pack),
325        Err(e) => eprintln!("warning: failed to load Fortran language pack: {e}"),
326    }
327    match nix_pack() {
328        Ok(pack) => registry.register(pack),
329        Err(e) => eprintln!("warning: failed to load Nix language pack: {e}"),
330    }
331    match svelte_pack() {
332        Ok(pack) => registry.register(pack),
333        Err(e) => eprintln!("warning: failed to load Svelte language pack: {e}"),
334    }
335    match fsharp_pack() {
336        Ok(pack) => registry.register(pack),
337        Err(e) => eprintln!("warning: failed to load F# language pack: {e}"),
338    }
339    match groovy_pack() {
340        Ok(pack) => registry.register(pack),
341        Err(e) => eprintln!("warning: failed to load Groovy language pack: {e}"),
342    }
343    match css_pack() {
344        Ok(pack) => registry.register(pack),
345        Err(e) => eprintln!("warning: failed to load CSS language pack: {e}"),
346    }
347    match html_pack() {
348        Ok(pack) => registry.register(pack),
349        Err(e) => eprintln!("warning: failed to load HTML language pack: {e}"),
350    }
351    match json_pack() {
352        Ok(pack) => registry.register(pack),
353        Err(e) => eprintln!("warning: failed to load JSON language pack: {e}"),
354    }
355    match xml_pack() {
356        Ok(pack) => registry.register(pack),
357        Err(e) => eprintln!("warning: failed to load XML language pack: {e}"),
358    }
359    match makefile_pack() {
360        Ok(pack) => registry.register(pack),
361        Err(e) => eprintln!("warning: failed to load Makefile language pack: {e}"),
362    }
363    match cmake_pack() {
364        Ok(pack) => registry.register(pack),
365        Err(e) => eprintln!("warning: failed to load CMake language pack: {e}"),
366    }
367    match graphql_pack() {
368        Ok(pack) => registry.register(pack),
369        Err(e) => eprintln!("warning: failed to load GraphQL language pack: {e}"),
370    }
371    match glsl_pack() {
372        Ok(pack) => registry.register(pack),
373        Err(e) => eprintln!("warning: failed to load GLSL language pack: {e}"),
374    }
375    match commonlisp_pack() {
376        Ok(pack) => registry.register(pack),
377        Err(e) => eprintln!("warning: failed to load Common Lisp language pack: {e}"),
378    }
379    match elm_pack() {
380        Ok(pack) => registry.register(pack),
381        Err(e) => eprintln!("warning: failed to load Elm language pack: {e}"),
382    }
383    match elisp_pack() {
384        Ok(pack) => registry.register(pack),
385        Err(e) => eprintln!("warning: failed to load Emacs Lisp language pack: {e}"),
386    }
387    match ini_pack() {
388        Ok(pack) => registry.register(pack),
389        Err(e) => eprintln!("warning: failed to load INI language pack: {e}"),
390    }
391    match tsx_pack() {
392        Ok(pack) => registry.register(pack),
393        Err(e) => eprintln!("warning: failed to load TSX language pack: {e}"),
394    }
395    match starlark_pack() {
396        Ok(pack) => registry.register(pack),
397        Err(e) => eprintln!("warning: failed to load Starlark language pack: {e}"),
398    }
399    match matlab_pack() {
400        Ok(pack) => registry.register(pack),
401        Err(e) => eprintln!("warning: failed to load MATLAB language pack: {e}"),
402    }
403    match markdown_pack() {
404        Ok(pack) => registry.register(pack),
405        Err(e) => eprintln!("warning: failed to load Markdown language pack: {e}"),
406    }
407    match clojure_pack() {
408        Ok(pack) => registry.register(pack),
409        Err(e) => eprintln!("warning: failed to load Clojure language pack: {e}"),
410    }
411    match cuda_pack() {
412        Ok(pack) => registry.register(pack),
413        Err(e) => eprintln!("warning: failed to load CUDA language pack: {e}"),
414    }
415    match pascal_pack() {
416        Ok(pack) => registry.register(pack),
417        Err(e) => eprintln!("warning: failed to load Pascal/Delphi language pack: {e}"),
418    }
419        match vb6_pack() {
420        Ok(pack) => registry.register(pack),
421        Err(e) => eprintln!("warning: failed to load VB6 language pack: {e}"),
422    }
423
424    Ok(registry)
425}
426
427fn python_pack() -> Result<LanguagePack> {
428    let grammar = tree_sitter_python::LANGUAGE.into();
429    LanguagePack::new_with_custom_edges(
430        "python",
431        vec![".py"],
432        grammar,
433        PYTHON_ENTITIES,
434        PYTHON_RELATIONS,
435        vec![CustomEdgeDef {
436            name: "DECORATED_BY".to_string(),
437            capture: "decorates".to_string(),
438        }],
439    )
440}
441
442fn rust_pack() -> Result<LanguagePack> {
443    let grammar = tree_sitter_rust::LANGUAGE.into();
444    LanguagePack::new("rust", vec![".rs"], grammar, RUST_ENTITIES, RUST_RELATIONS)
445}
446
447fn typescript_pack() -> Result<LanguagePack> {
448    let grammar = tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into();
449    LanguagePack::new(
450        "typescript",
451        vec![".ts"],
452        grammar,
453        TYPESCRIPT_ENTITIES,
454        TYPESCRIPT_RELATIONS,
455    )
456}
457
458fn javascript_pack() -> Result<LanguagePack> {
459    let grammar = tree_sitter_javascript::LANGUAGE.into();
460    LanguagePack::new(
461        "javascript",
462        vec![".js", ".jsx", ".mjs"],
463        grammar,
464        JAVASCRIPT_ENTITIES,
465        JAVASCRIPT_RELATIONS,
466    )
467}
468
469fn go_pack() -> Result<LanguagePack> {
470    let grammar = tree_sitter_go::LANGUAGE.into();
471    LanguagePack::new_with_custom_edges(
472        "go",
473        vec![".go"],
474        grammar,
475        GO_ENTITIES,
476        GO_RELATIONS,
477        vec![CustomEdgeDef {
478            name: "SPAWNS".to_string(),
479            capture: "goroutine".to_string(),
480        }],
481    )
482}
483
484fn java_pack() -> Result<LanguagePack> {
485    let grammar = tree_sitter_java::LANGUAGE.into();
486    LanguagePack::new(
487        "java",
488        vec![".java"],
489        grammar,
490        JAVA_ENTITIES,
491        JAVA_RELATIONS,
492    )
493}
494
495fn c_pack() -> Result<LanguagePack> {
496    let grammar = tree_sitter_c::LANGUAGE.into();
497    LanguagePack::new("c", vec![".c", ".h"], grammar, C_ENTITIES, C_RELATIONS)
498}
499
500fn cpp_pack() -> Result<LanguagePack> {
501    let grammar = tree_sitter_cpp::LANGUAGE.into();
502    LanguagePack::new(
503        "cpp",
504        vec![".cpp", ".cc", ".cxx", ".hpp", ".hxx", ".hh"],
505        grammar,
506        CPP_ENTITIES,
507        CPP_RELATIONS,
508    )
509}
510
511fn ruby_pack() -> Result<LanguagePack> {
512    let grammar = tree_sitter_ruby::LANGUAGE.into();
513    LanguagePack::new(
514        "ruby",
515        vec![".rb", ".rake", ".gemspec"],
516        grammar,
517        RUBY_ENTITIES,
518        RUBY_RELATIONS,
519    )
520}
521
522fn php_pack() -> Result<LanguagePack> {
523    let grammar = tree_sitter_php::LANGUAGE_PHP.into();
524    LanguagePack::new("php", vec![".php"], grammar, PHP_ENTITIES, PHP_RELATIONS)
525}
526
527fn swift_pack() -> Result<LanguagePack> {
528    let grammar = tree_sitter_swift::LANGUAGE.into();
529    LanguagePack::new(
530        "swift",
531        vec![".swift"],
532        grammar,
533        SWIFT_ENTITIES,
534        SWIFT_RELATIONS,
535    )
536}
537
538fn kotlin_pack() -> Result<LanguagePack> {
539    let grammar = tree_sitter_kotlin_ng::LANGUAGE.into();
540    LanguagePack::new(
541        "kotlin",
542        vec![".kt", ".kts"],
543        grammar,
544        KOTLIN_ENTITIES,
545        KOTLIN_RELATIONS,
546    )
547}
548
549fn csharp_pack() -> Result<LanguagePack> {
550    let grammar = tree_sitter_c_sharp::LANGUAGE.into();
551    LanguagePack::new(
552        "csharp",
553        vec![".cs"],
554        grammar,
555        CSHARP_ENTITIES,
556        CSHARP_RELATIONS,
557    )
558}
559
560fn scala_pack() -> Result<LanguagePack> {
561    let grammar = tree_sitter_scala::LANGUAGE.into();
562    LanguagePack::new(
563        "scala",
564        vec![".scala", ".sc"],
565        grammar,
566        SCALA_ENTITIES,
567        SCALA_RELATIONS,
568    )
569}
570
571fn lua_pack() -> Result<LanguagePack> {
572    let grammar = tree_sitter_lua::LANGUAGE.into();
573    LanguagePack::new("lua", vec![".lua"], grammar, LUA_ENTITIES, LUA_RELATIONS)
574}
575
576fn zig_pack() -> Result<LanguagePack> {
577    let grammar = tree_sitter_zig::LANGUAGE.into();
578    LanguagePack::new("zig", vec![".zig"], grammar, ZIG_ENTITIES, ZIG_RELATIONS)
579}
580
581fn elixir_pack() -> Result<LanguagePack> {
582    let grammar = tree_sitter_elixir::LANGUAGE.into();
583    LanguagePack::new(
584        "elixir",
585        vec![".ex", ".exs"],
586        grammar,
587        ELIXIR_ENTITIES,
588        ELIXIR_RELATIONS,
589    )
590}
591
592fn dart_pack() -> Result<LanguagePack> {
593    let grammar = tree_sitter_dart::LANGUAGE.into();
594    LanguagePack::new(
595        "dart",
596        vec![".dart"],
597        grammar,
598        DART_ENTITIES,
599        DART_RELATIONS,
600    )
601}
602
603fn objc_pack() -> Result<LanguagePack> {
604    let grammar = tree_sitter_objc::LANGUAGE.into();
605    LanguagePack::new(
606        "objc",
607        vec![".m", ".mm"],
608        grammar,
609        OBJC_ENTITIES,
610        OBJC_RELATIONS,
611    )
612}
613
614fn haskell_pack() -> Result<LanguagePack> {
615    let grammar = tree_sitter_haskell::LANGUAGE.into();
616    LanguagePack::new(
617        "haskell",
618        vec![".hs", ".lhs"],
619        grammar,
620        HASKELL_ENTITIES,
621        HASKELL_RELATIONS,
622    )
623}
624
625fn perl_pack() -> Result<LanguagePack> {
626    let grammar = tree_sitter_perl::LANGUAGE.into();
627    LanguagePack::new(
628        "perl",
629        vec![".pl", ".pm", ".t"],
630        grammar,
631        PERL_ENTITIES,
632        PERL_RELATIONS,
633    )
634}
635
636fn r_pack() -> Result<LanguagePack> {
637    let grammar = tree_sitter_r::LANGUAGE.into();
638    LanguagePack::new(
639        "r",
640        vec![".r", ".R", ".Rmd"],
641        grammar,
642        R_ENTITIES,
643        R_RELATIONS,
644    )
645}
646
647fn ocaml_pack() -> Result<LanguagePack> {
648    let grammar = tree_sitter_ocaml::LANGUAGE_OCAML.into();
649    LanguagePack::new(
650        "ocaml",
651        vec![".ml", ".mli"],
652        grammar,
653        OCAML_ENTITIES,
654        OCAML_RELATIONS,
655    )
656}
657
658fn bash_pack() -> Result<LanguagePack> {
659    let grammar = tree_sitter_bash::LANGUAGE.into();
660    LanguagePack::new(
661        "bash",
662        vec![".sh", ".bash", ".zsh"],
663        grammar,
664        BASH_ENTITIES,
665        BASH_RELATIONS,
666    )
667}
668
669fn sql_pack() -> Result<LanguagePack> {
670    let grammar = tree_sitter_sequel::LANGUAGE.into();
671    LanguagePack::new("sql", vec![".sql"], grammar, SQL_ENTITIES, SQL_RELATIONS)
672}
673
674fn julia_pack() -> Result<LanguagePack> {
675    let grammar = tree_sitter_julia::LANGUAGE.into();
676    LanguagePack::new(
677        "julia",
678        vec![".jl"],
679        grammar,
680        JULIA_ENTITIES,
681        JULIA_RELATIONS,
682    )
683}
684
685fn proto_pack() -> Result<LanguagePack> {
686    let grammar = tree_sitter_proto::LANGUAGE.into();
687    LanguagePack::new(
688        "proto",
689        vec![".proto"],
690        grammar,
691        PROTO_ENTITIES,
692        PROTO_RELATIONS,
693    )
694}
695
696fn powershell_pack() -> Result<LanguagePack> {
697    let grammar = tree_sitter_powershell::LANGUAGE.into();
698    LanguagePack::new(
699        "powershell",
700        vec![".ps1", ".psm1", ".psd1"],
701        grammar,
702        POWERSHELL_ENTITIES,
703        POWERSHELL_RELATIONS,
704    )
705}
706
707fn verilog_pack() -> Result<LanguagePack> {
708    let grammar = tree_sitter_verilog::LANGUAGE.into();
709    LanguagePack::new(
710        "verilog",
711        vec![".v", ".sv", ".svh", ".vh"],
712        grammar,
713        VERILOG_ENTITIES,
714        VERILOG_RELATIONS,
715    )
716}
717
718fn hcl_pack() -> Result<LanguagePack> {
719    let grammar = tree_sitter_hcl::LANGUAGE.into();
720    LanguagePack::new(
721        "hcl",
722        vec![".hcl", ".tf", ".tfvars"],
723        grammar,
724        HCL_ENTITIES,
725        HCL_RELATIONS,
726    )
727}
728
729fn toml_pack() -> Result<LanguagePack> {
730    let grammar = tree_sitter_toml_ng::LANGUAGE.into();
731    LanguagePack::new(
732        "toml",
733        vec![".toml"],
734        grammar,
735        TOML_ENTITIES,
736        TOML_RELATIONS,
737    )
738}
739
740fn yaml_pack() -> Result<LanguagePack> {
741    let grammar = tree_sitter_yaml::LANGUAGE.into();
742    LanguagePack::new(
743        "yaml",
744        vec![".yml", ".yaml"],
745        grammar,
746        YAML_ENTITIES,
747        YAML_RELATIONS,
748    )
749}
750
751fn erlang_pack() -> Result<LanguagePack> {
752    let grammar = tree_sitter_erlang::LANGUAGE.into();
753    LanguagePack::new(
754        "erlang",
755        vec![".erl", ".hrl"],
756        grammar,
757        ERLANG_ENTITIES,
758        ERLANG_RELATIONS,
759    )
760}
761
762fn dockerfile_pack() -> Result<LanguagePack> {
763    let grammar = tree_sitter_containerfile::LANGUAGE.into();
764    LanguagePack::new(
765        "dockerfile",
766        vec!["Dockerfile", "Containerfile", ".dockerfile"],
767        grammar,
768        DOCKERFILE_ENTITIES,
769        DOCKERFILE_RELATIONS,
770    )
771}
772
773fn fortran_pack() -> Result<LanguagePack> {
774    let grammar = tree_sitter_fortran::LANGUAGE.into();
775    LanguagePack::new(
776        "fortran",
777        vec![".f90", ".f95", ".f03", ".f08", ".f", ".for"],
778        grammar,
779        FORTRAN_ENTITIES,
780        FORTRAN_RELATIONS,
781    )
782}
783
784fn nix_pack() -> Result<LanguagePack> {
785    let grammar = tree_sitter_nix::LANGUAGE.into();
786    LanguagePack::new("nix", vec![".nix"], grammar, NIX_ENTITIES, NIX_RELATIONS)
787}
788
789fn svelte_pack() -> Result<LanguagePack> {
790    let grammar = tree_sitter_svelte_ng::LANGUAGE.into();
791    LanguagePack::new(
792        "svelte",
793        vec![".svelte"],
794        grammar,
795        SVELTE_ENTITIES,
796        SVELTE_RELATIONS,
797    )
798}
799
800fn fsharp_pack() -> Result<LanguagePack> {
801    let grammar = tree_sitter_fsharp::LANGUAGE_FSHARP.into();
802    LanguagePack::new(
803        "fsharp",
804        vec![".fs", ".fsi", ".fsx"],
805        grammar,
806        FSHARP_ENTITIES,
807        FSHARP_RELATIONS,
808    )
809}
810
811fn groovy_pack() -> Result<LanguagePack> {
812    let grammar = tree_sitter_groovy::LANGUAGE.into();
813    LanguagePack::new(
814        "groovy",
815        vec![".groovy", ".gradle"],
816        grammar,
817        GROOVY_ENTITIES,
818        GROOVY_RELATIONS,
819    )
820}
821
822fn css_pack() -> Result<LanguagePack> {
823    let grammar = tree_sitter_css::LANGUAGE.into();
824    LanguagePack::new("css", vec![".css"], grammar, CSS_ENTITIES, CSS_RELATIONS)
825}
826
827fn html_pack() -> Result<LanguagePack> {
828    let grammar = tree_sitter_html::LANGUAGE.into();
829    LanguagePack::new(
830        "html",
831        vec![".html", ".htm"],
832        grammar,
833        HTML_ENTITIES,
834        HTML_RELATIONS,
835    )
836}
837
838fn json_pack() -> Result<LanguagePack> {
839    let grammar = tree_sitter_json::LANGUAGE.into();
840    LanguagePack::new(
841        "json",
842        vec![".json"],
843        grammar,
844        JSON_ENTITIES,
845        JSON_RELATIONS,
846    )
847}
848
849fn xml_pack() -> Result<LanguagePack> {
850    let grammar = tree_sitter_xml::LANGUAGE_XML.into();
851    LanguagePack::new(
852        "xml",
853        vec![".xml", ".xsl", ".xsd", ".svg", ".plist"],
854        grammar,
855        XML_ENTITIES,
856        XML_RELATIONS,
857    )
858}
859
860fn makefile_pack() -> Result<LanguagePack> {
861    let grammar = tree_sitter_make::LANGUAGE.into();
862    LanguagePack::new(
863        "makefile",
864        vec!["Makefile", "makefile", "GNUmakefile", ".mk"],
865        grammar,
866        MAKEFILE_ENTITIES,
867        MAKEFILE_RELATIONS,
868    )
869}
870
871fn cmake_pack() -> Result<LanguagePack> {
872    let grammar = tree_sitter_cmake::LANGUAGE.into();
873    LanguagePack::new(
874        "cmake",
875        vec!["CMakeLists.txt", ".cmake"],
876        grammar,
877        CMAKE_ENTITIES,
878        CMAKE_RELATIONS,
879    )
880}
881
882fn graphql_pack() -> Result<LanguagePack> {
883    let grammar = tree_sitter_graphql::LANGUAGE.into();
884    LanguagePack::new(
885        "graphql",
886        vec![".graphql", ".gql"],
887        grammar,
888        GRAPHQL_ENTITIES,
889        GRAPHQL_RELATIONS,
890    )
891}
892
893fn glsl_pack() -> Result<LanguagePack> {
894    let grammar = tree_sitter_glsl::LANGUAGE_GLSL.into();
895    LanguagePack::new(
896        "glsl",
897        vec![".glsl", ".vert", ".frag", ".geom", ".comp"],
898        grammar,
899        GLSL_ENTITIES,
900        GLSL_RELATIONS,
901    )
902}
903
904fn commonlisp_pack() -> Result<LanguagePack> {
905    let grammar = tree_sitter_commonlisp::LANGUAGE_COMMONLISP.into();
906    LanguagePack::new(
907        "commonlisp",
908        vec![".lisp", ".lsp", ".cl", ".asd"],
909        grammar,
910        COMMONLISP_ENTITIES,
911        COMMONLISP_RELATIONS,
912    )
913}
914
915fn elm_pack() -> Result<LanguagePack> {
916    let grammar = tree_sitter_elm::LANGUAGE.into();
917    LanguagePack::new("elm", vec![".elm"], grammar, ELM_ENTITIES, ELM_RELATIONS)
918}
919
920fn elisp_pack() -> Result<LanguagePack> {
921    let grammar = tree_sitter_elisp::LANGUAGE.into();
922    LanguagePack::new(
923        "elisp",
924        vec![".el"],
925        grammar,
926        ELISP_ENTITIES,
927        ELISP_RELATIONS,
928    )
929}
930
931fn ini_pack() -> Result<LanguagePack> {
932    let grammar = tree_sitter_ini::LANGUAGE.into();
933    LanguagePack::new(
934        "ini",
935        vec![".ini", ".cfg", ".conf"],
936        grammar,
937        INI_ENTITIES,
938        INI_RELATIONS,
939    )
940}
941
942fn tsx_pack() -> Result<LanguagePack> {
943    let grammar = tree_sitter_typescript::LANGUAGE_TSX.into();
944    LanguagePack::new("tsx", vec![".tsx"], grammar, TSX_ENTITIES, TSX_RELATIONS)
945}
946
947fn starlark_pack() -> Result<LanguagePack> {
948    let grammar = tree_sitter_starlark::LANGUAGE.into();
949    LanguagePack::new(
950        "starlark",
951        vec![".bzl", ".star", "BUILD", "BUILD.bazel", "WORKSPACE"],
952        grammar,
953        STARLARK_ENTITIES,
954        STARLARK_RELATIONS,
955    )
956}
957
958fn matlab_pack() -> Result<LanguagePack> {
959    let grammar = tree_sitter_matlab::LANGUAGE.into();
960    LanguagePack::new(
961        "matlab",
962        vec![".mlx", ".mat"],
963        grammar,
964        MATLAB_ENTITIES,
965        MATLAB_RELATIONS,
966    )
967}
968
969fn markdown_pack() -> Result<LanguagePack> {
970    let grammar = tree_sitter_md::LANGUAGE.into();
971    LanguagePack::new(
972        "markdown",
973        vec![".md", ".markdown"],
974        grammar,
975        MARKDOWN_ENTITIES,
976        MARKDOWN_RELATIONS,
977    )
978}
979
980fn clojure_pack() -> Result<LanguagePack> {
981    let grammar = tree_sitter_clojure_orchard::LANGUAGE.into();
982    LanguagePack::new(
983        "clojure",
984        vec![".clj", ".cljs", ".cljc", ".edn"],
985        grammar,
986        CLOJURE_ENTITIES,
987        CLOJURE_RELATIONS,
988    )
989}
990
991fn cuda_pack() -> Result<LanguagePack> {
992    let grammar = tree_sitter_cuda::LANGUAGE.into();
993    LanguagePack::new(
994        "cuda",
995        vec![".cu", ".cuh"],
996        grammar,
997        CUDA_ENTITIES,
998        CUDA_RELATIONS,
999    )
1000}
1001
1002fn pascal_pack() -> Result<LanguagePack> {
1003    let grammar = tree_sitter_pascal::LANGUAGE.into();
1004    LanguagePack::new(
1005        "pascal",
1006        vec![".pas", ".pp", ".dpr", ".dpk", ".inc", ".lpr"],
1007        grammar,
1008        PASCAL_ENTITIES,
1009        PASCAL_RELATIONS,
1010    )
1011}
1012
1013fn vb6_pack() -> Result<LanguagePack> {
1014    let grammar = tree_sitter_vb6::language();
1015    LanguagePack::new(
1016        "vb6",
1017        vec![".bas", ".cls", ".frm"],
1018        grammar,
1019        VB6_ENTITIES,
1020        VB6_RELATIONS,
1021    )
1022}
1023
1024#[cfg(test)]
1025mod tests {
1026    use super::*;
1027
1028    #[test]
1029    fn test_all_packs_load() {
1030        // Verify bundled_registry succeeds (individual packs may warn but shouldn't panic)
1031        let registry = bundled_registry().expect("bundled_registry should succeed");
1032        assert!(
1033            registry.languages().count() > 0,
1034            "registry should have at least one language"
1035        );
1036    }
1037
1038    #[test]
1039    fn test_powershell_pack() {
1040        powershell_pack().expect("PowerShell pack should load");
1041    }
1042
1043    #[test]
1044    fn test_verilog_pack() {
1045        verilog_pack().expect("Verilog pack should load");
1046    }
1047
1048    #[test]
1049    fn test_hcl_pack() {
1050        hcl_pack().expect("HCL pack should load");
1051    }
1052
1053    #[test]
1054    fn test_toml_pack() {
1055        toml_pack().expect("TOML pack should load");
1056    }
1057
1058    #[test]
1059    fn test_yaml_pack() {
1060        yaml_pack().expect("YAML pack should load");
1061    }
1062
1063    #[test]
1064    fn test_erlang_pack() {
1065        erlang_pack().expect("Erlang pack should load");
1066    }
1067
1068    #[test]
1069    fn test_dockerfile_pack() {
1070        dockerfile_pack().expect("Dockerfile pack should load");
1071    }
1072
1073    #[test]
1074    fn test_fortran_pack() {
1075        fortran_pack().expect("Fortran pack should load");
1076    }
1077
1078    #[test]
1079    fn test_nix_pack() {
1080        nix_pack().expect("Nix pack should load");
1081    }
1082
1083    #[test]
1084    fn test_svelte_pack() {
1085        svelte_pack().expect("Svelte pack should load");
1086    }
1087
1088    #[test]
1089    fn test_fsharp_pack() {
1090        fsharp_pack().expect("F# pack should load");
1091    }
1092
1093    #[test]
1094    fn test_groovy_pack() {
1095        groovy_pack().expect("Groovy pack should load");
1096    }
1097
1098    #[test]
1099    fn test_css_pack() {
1100        css_pack().expect("CSS pack should load");
1101    }
1102
1103    #[test]
1104    fn test_html_pack() {
1105        html_pack().expect("HTML pack should load");
1106    }
1107
1108    #[test]
1109    fn test_json_pack() {
1110        json_pack().expect("JSON pack should load");
1111    }
1112
1113    #[test]
1114    fn test_xml_pack() {
1115        xml_pack().expect("XML pack should load");
1116    }
1117
1118    #[test]
1119    fn test_makefile_pack() {
1120        makefile_pack().expect("Makefile pack should load");
1121    }
1122
1123    #[test]
1124    fn test_cmake_pack() {
1125        cmake_pack().expect("CMake pack should load");
1126    }
1127
1128    #[test]
1129    fn test_graphql_pack() {
1130        graphql_pack().expect("GraphQL pack should load");
1131    }
1132
1133    #[test]
1134    fn test_glsl_pack() {
1135        glsl_pack().expect("GLSL pack should load");
1136    }
1137
1138    #[test]
1139    fn test_commonlisp_pack() {
1140        commonlisp_pack().expect("Common Lisp pack should load");
1141    }
1142
1143    #[test]
1144    fn test_elm_pack() {
1145        elm_pack().expect("Elm pack should load");
1146    }
1147
1148    #[test]
1149    fn test_elisp_pack() {
1150        elisp_pack().expect("Emacs Lisp pack should load");
1151    }
1152
1153    #[test]
1154    fn test_ini_pack() {
1155        ini_pack().expect("INI pack should load");
1156    }
1157
1158    #[test]
1159    fn test_tsx_pack() {
1160        tsx_pack().expect("TSX pack should load");
1161    }
1162
1163    #[test]
1164    fn test_matlab_pack() {
1165        matlab_pack().expect("MATLAB pack should load");
1166    }
1167
1168    #[test]
1169    fn test_markdown_pack() {
1170        markdown_pack().expect("Markdown pack should load");
1171    }
1172
1173    #[test]
1174    fn test_clojure_pack() {
1175        clojure_pack().expect("Clojure pack should load");
1176    }
1177
1178    #[test]
1179    fn test_cuda_pack() {
1180        cuda_pack().expect("CUDA pack should load");
1181    }
1182
1183    #[test]
1184        fn test_vb6_pack() {
1185        vb6_pack().expect("VB6 pack should load");
1186    }
1187
1188    #[test]
1189        fn test_vb6_e2e_smoke() {
1190        let pack = vb6_pack().expect("VB6 pack should load");
1191        // Note: the tree-sitter-vb6 grammar parses `Call Foo(args)` incorrectly (Call becomes
1192        // the function name); use direct call syntax `Foo(args)` instead.
1193        let src = r#"VERSION 1.0 CLASS
1194BEGIN
1195  MultiUse = -1
1196END
1197Attribute VB_Name = "TestClass"
1198Option Explicit
1199
1200Private mName As String
1201
1202Public Sub Initialize(name As String)
1203    mName = name
1204End Sub
1205
1206Public Function GetName() As String
1207    GetName = mName
1208End Function
1209
1210Private Sub Helper()
1211    Dim result As String
1212    result = GetName()
1213    Initialize(result)
1214End Sub
1215"#;
1216        let extraction =
1217            infigraph_core::extract::extract_file("TestClass.cls", src.as_bytes(), &pack)
1218                .expect("extract_file should succeed");
1219
1220        let symbol_names: Vec<&str> = extraction.symbols.iter().map(|s| s.name.as_str()).collect();
1221        println!("Symbols: {:?}", symbol_names);
1222        println!(
1223            "Symbol kinds: {:?}",
1224            extraction
1225                .symbols
1226                .iter()
1227                .map(|s| format!("{} ({:?})", s.name, s.kind))
1228                .collect::<Vec<_>>()
1229        );
1230        println!(
1231            "Relations: {:?}",
1232            extraction
1233                .relations
1234                .iter()
1235                .map(|r| format!("{} -> {}", r.source_id, r.target_id))
1236                .collect::<Vec<_>>()
1237        );
1238
1239        // Verify module symbol
1240        let module = extraction.symbols.iter().find(|s| s.name == "TestClass");
1241        assert!(
1242            module.is_some(),
1243            "Expected Module symbol 'TestClass', got: {:?}",
1244            symbol_names
1245        );
1246        assert_eq!(
1247            module.unwrap().kind,
1248            infigraph_core::model::SymbolKind::Module
1249        );
1250
1251        // Verify functions/subs
1252        assert!(
1253            symbol_names.contains(&"Initialize"),
1254            "Expected 'Initialize'"
1255        );
1256        assert!(symbol_names.contains(&"GetName"), "Expected 'GetName'");
1257        assert!(symbol_names.contains(&"Helper"), "Expected 'Helper'");
1258
1259        // Verify variable
1260        assert!(symbol_names.contains(&"mName"), "Expected 'mName'");
1261
1262        // Verify call relations exist — Helper calls GetName and Initialize
1263        assert!(
1264            !extraction.relations.is_empty(),
1265            "Expected call relations from Helper"
1266        );
1267
1268        let relation_pairs: Vec<(&str, &str)> = extraction
1269            .relations
1270            .iter()
1271            .map(|r| (r.source_id.as_str(), r.target_id.as_str()))
1272            .collect();
1273        println!("Relation pairs: {:?}", relation_pairs);
1274
1275        let helper_calls_getname = extraction
1276            .relations
1277            .iter()
1278            .any(|r| r.source_id.ends_with("::Helper") && r.target_id.ends_with("::GetName"));
1279        let helper_calls_initialize = extraction
1280            .relations
1281            .iter()
1282            .any(|r| r.source_id.ends_with("::Helper") && r.target_id.ends_with("::Initialize"));
1283
1284        assert!(
1285            helper_calls_getname,
1286            "Expected Helper -> GetName call edge, got: {:?}",
1287            relation_pairs
1288        );
1289        assert!(
1290            helper_calls_initialize,
1291            "Expected Helper -> Initialize call edge, got: {:?}",
1292            relation_pairs
1293        );
1294    }
1295
1296    #[test]
1297    fn test_sql_table_lineage() {
1298        let pack = sql_pack().unwrap();
1299        let sql = b"CREATE TABLE output AS SELECT col1 FROM source_a INNER JOIN source_b ON source_a.id = source_b.id;
1300WITH cte1 AS (SELECT * FROM base_table), cte2 AS (SELECT * FROM cte1) SELECT * FROM cte2;
1301INSERT INTO target_table SELECT * FROM input_table;";
1302
1303        let extraction = infigraph_core::extract::extract_file("test.sql", sql, &pack).unwrap();
1304
1305        let sym_names: Vec<&str> = extraction.symbols.iter().map(|s| s.name.as_str()).collect();
1306        assert!(
1307            sym_names.contains(&"output"),
1308            "expected CREATE TABLE output"
1309        );
1310        assert!(sym_names.contains(&"cte1"), "expected CTE cte1");
1311        assert!(sym_names.contains(&"cte2"), "expected CTE cte2");
1312
1313        let has_edge = |src_suffix: &str, tgt_suffix: &str| {
1314            extraction
1315                .relations
1316                .iter()
1317                .any(|r| r.source_id.ends_with(src_suffix) && r.target_id.ends_with(tgt_suffix))
1318        };
1319        assert!(
1320            has_edge("::output", "::source_a"),
1321            "expected output -> source_a"
1322        );
1323        assert!(
1324            has_edge("::output", "::source_b"),
1325            "expected output -> source_b"
1326        );
1327        assert!(
1328            has_edge("::cte1", "::base_table"),
1329            "expected cte1 -> base_table"
1330        );
1331        assert!(has_edge("::cte2", "::cte1"), "expected cte2 -> cte1");
1332    }
1333
1334    #[test]
1335    fn test_sql_spark_extraction_real() {
1336        let sample_dir = std::path::Path::new("/tmp/efp-group-sql-samples");
1337        if !sample_dir.exists() {
1338            println!("skipping — samples not found");
1339            return;
1340        }
1341        let pack = sql_pack().unwrap();
1342        for entry in std::fs::read_dir(sample_dir).unwrap().flatten() {
1343            let p = entry.path();
1344            if p.extension().and_then(|e| e.to_str()) != Some("sql") {
1345                continue;
1346            }
1347            let Ok(src) = std::fs::read(&p) else {
1348                continue;
1349            };
1350            if src.is_empty() {
1351                continue;
1352            }
1353            let fname = p.file_name().unwrap().to_string_lossy().to_string();
1354            match infigraph_core::extract::extract_file(&fname, &src, &pack) {
1355                Ok(e) => println!(
1356                    "{fname}: {} symbols, {} relations",
1357                    e.symbols.len(),
1358                    e.relations.len()
1359                ),
1360                Err(err) => println!("{fname}: EXTRACT ERROR: {err}"),
1361            }
1362        }
1363    }
1364
1365    #[test]
1366    fn test_sql_notebook_file_level_refs() {
1367        let pack = sql_pack().unwrap();
1368        let sql = b"-- Databricks notebook source
1369SELECT * FROM fraud_360_rpt WHERE year='2023';
1370
1371-- COMMAND ----------
1372
1373SELECT a.*, b.score FROM risk_assessment a
1374LEFT JOIN risk_rules b ON a.id = b.assessment_id;";
1375
1376        let extraction = infigraph_core::extract::extract_file("notebook.sql", sql, &pack).unwrap();
1377
1378        assert!(
1379            !extraction.relations.is_empty(),
1380            "expected file-level relations from notebook SQL"
1381        );
1382        let has_edge = |tgt: &str| {
1383            extraction
1384                .relations
1385                .iter()
1386                .any(|r| r.target_id.ends_with(tgt))
1387        };
1388        assert!(has_edge("::fraud_360_rpt"), "expected ref to fraud_360_rpt");
1389        assert!(
1390            has_edge("::risk_assessment"),
1391            "expected ref to risk_assessment"
1392        );
1393        assert!(has_edge("::risk_rules"), "expected ref to risk_rules");
1394
1395        let file_sourced = extraction
1396            .relations
1397            .iter()
1398            .any(|r| r.source_id.contains("notebook.sql"));
1399        assert!(
1400            file_sourced,
1401            "expected file-level source for notebook relations"
1402        );
1403    }
1404
1405    #[test]
1406    fn test_sql_dialect_coverage() {
1407        let pack = sql_pack().unwrap();
1408
1409        #[allow(clippy::type_complexity)]
1410        let cases: Vec<(&str, &[u8], &[&str], &[(&str, &str)])> = vec![
1411            // (label, sql, expected_targets, expected_edges as (src_suffix, tgt_suffix))
1412
1413            // Spark SQL
1414            (
1415                "spark_ctas",
1416                b"CREATE TABLE output AS SELECT * FROM src_a JOIN src_b ON a.id = b.id;" as &[u8],
1417                &["src_a", "src_b"],
1418                &[("::output", "::src_a"), ("::output", "::src_b")],
1419            ),
1420            (
1421                "spark_insert_overwrite",
1422                b"INSERT OVERWRITE TABLE target SELECT * FROM source;",
1423                &["source"],
1424                &[],
1425            ),
1426            (
1427                "spark_cte",
1428                b"WITH stg AS (SELECT * FROM raw_events) SELECT * FROM stg;",
1429                &["raw_events", "stg"],
1430                &[("::stg", "::raw_events")],
1431            ),
1432            // T-SQL / SQL Server
1433            (
1434                "tsql_select_into",
1435                b"SELECT * INTO new_table FROM old_table;",
1436                &["old_table"],
1437                &[],
1438            ),
1439            (
1440                "tsql_cte_insert",
1441                b"WITH cte AS (SELECT * FROM src) INSERT INTO tgt SELECT * FROM cte;",
1442                &["src", "cte"],
1443                &[("::cte", "::src")],
1444            ),
1445            // PostgreSQL
1446            (
1447                "pg_ctas",
1448                b"CREATE TABLE summary AS SELECT dept, count(*) FROM employees GROUP BY dept;",
1449                &["employees"],
1450                &[("::summary", "::employees")],
1451            ),
1452            (
1453                "pg_with_insert",
1454                b"WITH src AS (SELECT * FROM raw) INSERT INTO clean SELECT * FROM src;",
1455                &["raw", "src"],
1456                &[("::src", "::raw")],
1457            ),
1458            // MySQL
1459            (
1460                "mysql_insert_ignore",
1461                b"INSERT IGNORE INTO users SELECT * FROM staging_users;",
1462                &["staging_users"],
1463                &[],
1464            ),
1465            // BigQuery style
1466            (
1467                "bq_create_or_replace",
1468                b"CREATE OR REPLACE TABLE output AS SELECT * FROM input;",
1469                &["input"],
1470                &[],
1471            ),
1472            // Common patterns
1473            (
1474                "subquery",
1475                b"SELECT * FROM (SELECT id FROM raw_data) sub;",
1476                &["raw_data"],
1477                &[],
1478            ),
1479            (
1480                "multi_join",
1481                b"SELECT * FROM a JOIN b ON a.id = b.id LEFT JOIN c ON b.id = c.id;",
1482                &["a", "b", "c"],
1483                &[],
1484            ),
1485            (
1486                "union_all",
1487                b"SELECT * FROM t1 UNION ALL SELECT * FROM t2;",
1488                &["t1", "t2"],
1489                &[],
1490            ),
1491        ];
1492
1493        let mut failures = Vec::new();
1494
1495        for (label, sql, expected_targets, expected_edges) in &cases {
1496            let extraction = infigraph_core::extract::extract_file("test.sql", sql, &pack).unwrap();
1497            let all_targets: Vec<&str> = extraction
1498                .relations
1499                .iter()
1500                .map(|r| r.target_id.as_str())
1501                .collect();
1502
1503            for tgt in *expected_targets {
1504                let suffix = format!("::{}", tgt);
1505                if !extraction
1506                    .relations
1507                    .iter()
1508                    .any(|r| r.target_id.ends_with(&suffix))
1509                {
1510                    failures.push(format!(
1511                        "{}: missing target ref to {}, got {:?}",
1512                        label, tgt, all_targets
1513                    ));
1514                }
1515            }
1516
1517            for (src_suf, tgt_suf) in *expected_edges {
1518                if !extraction
1519                    .relations
1520                    .iter()
1521                    .any(|r| r.source_id.ends_with(src_suf) && r.target_id.ends_with(tgt_suf))
1522                {
1523                    let edges: Vec<_> = extraction
1524                        .relations
1525                        .iter()
1526                        .map(|r| format!("{} -> {}", r.source_id, r.target_id))
1527                        .collect();
1528                    failures.push(format!(
1529                        "{}: missing edge {} -> {}, got {:?}",
1530                        label, src_suf, tgt_suf, edges
1531                    ));
1532                }
1533            }
1534        }
1535
1536        if !failures.is_empty() {
1537            panic!("SQL dialect coverage failures:\n{}", failures.join("\n"));
1538        }
1539    }
1540
1541    #[test]
1542    fn test_sql_notebook_formats() {
1543        let pack = sql_pack().unwrap();
1544
1545        let cases: Vec<(&str, &[u8], &[&str])> = vec![
1546            // Databricks notebook
1547            ("databricks", b"-- Databricks notebook source\nSELECT * FROM table_a;\n-- COMMAND ----------\nSELECT * FROM table_b;" as &[u8],
1548             &["table_a", "table_b"]),
1549
1550            // Jupyter-style: SQL cells are just raw SQL (no magic prefix in .sql export)
1551            ("jupyter_plain", b"SELECT * FROM dataset_1;\nSELECT * FROM dataset_2 JOIN dataset_3 ON d2.id = d3.id;",
1552             &["dataset_1", "dataset_2", "dataset_3"]),
1553
1554            // Zeppelin notebook style (paragraph markers)
1555            ("zeppelin", b"%sql\nSELECT * FROM zep_table_1;\n\n%sql\nSELECT a.* FROM zep_table_2 a LEFT JOIN zep_table_3 b ON a.id = b.id;",
1556             &["zep_table_1", "zep_table_2", "zep_table_3"]),
1557
1558            // dbt-style Jinja (jinja tags parse as errors but table refs in FROM survive)
1559            ("dbt_ref", b"SELECT * FROM {{ ref('stg_orders') }}\nJOIN raw_customers ON orders.cust_id = raw_customers.id;",
1560             &["raw_customers"]),
1561
1562            // Mixed DDL + bare SELECT
1563            ("mixed", b"CREATE TABLE output AS SELECT * FROM src;\nSELECT * FROM standalone_ref;",
1564             &["src", "standalone_ref"]),
1565        ];
1566
1567        let mut failures = Vec::new();
1568
1569        for (label, sql, expected_targets) in &cases {
1570            let extraction =
1571                infigraph_core::extract::extract_file("notebook.sql", sql, &pack).unwrap();
1572            let all_targets: Vec<String> = extraction
1573                .relations
1574                .iter()
1575                .map(|r| r.target_id.clone())
1576                .collect();
1577
1578            for tgt in *expected_targets {
1579                let suffix = format!("::{}", tgt);
1580                if !extraction
1581                    .relations
1582                    .iter()
1583                    .any(|r| r.target_id.ends_with(&suffix))
1584                {
1585                    failures.push(format!(
1586                        "{}: missing target ref to {}, got {:?}",
1587                        label, tgt, all_targets
1588                    ));
1589                }
1590            }
1591
1592            let all_file_sourced = extraction
1593                .relations
1594                .iter()
1595                .filter(|r| !r.source_id.contains("::"))
1596                .count()
1597                == 0;
1598            if !all_file_sourced {
1599                let non_qualified: Vec<_> = extraction
1600                    .relations
1601                    .iter()
1602                    .filter(|r| !r.source_id.contains("::"))
1603                    .map(|r| r.source_id.as_str())
1604                    .collect();
1605                if !non_qualified.is_empty() {
1606                    failures.push(format!(
1607                        "{}: source_ids without '::': {:?}",
1608                        label, non_qualified
1609                    ));
1610                }
1611            }
1612        }
1613
1614        if !failures.is_empty() {
1615            panic!("Notebook format failures:\n{}", failures.join("\n"));
1616        }
1617    }
1618
1619    #[test]
1620    fn test_sql_complex_nested_and_dml() {
1621        let pack = sql_pack().unwrap();
1622
1623        #[allow(clippy::type_complexity)]
1624        let cases: Vec<(&str, &[u8], &[&str], &[(&str, &str)])> = vec![
1625            // Nested subqueries in FROM
1626            ("nested_subquery",
1627             b"SELECT * FROM (SELECT id FROM (SELECT id FROM deep_source) inner_q) outer_q;" as &[u8],
1628             &["deep_source"], &[]),
1629
1630            // CTE chain: cte1 -> raw, cte2 -> cte1, cte3 -> cte2 + extra
1631            ("cte_chain",
1632             b"WITH cte1 AS (SELECT * FROM raw_data), \
1633               cte2 AS (SELECT * FROM cte1), \
1634               cte3 AS (SELECT a.* FROM cte2 a JOIN dim_table b ON a.id = b.id) \
1635               SELECT * FROM cte3;",
1636             &["raw_data", "cte1", "cte2", "dim_table", "cte3"],
1637             &[("::cte1", "::raw_data"), ("::cte2", "::cte1"), ("::cte3", "::cte2"), ("::cte3", "::dim_table")]),
1638
1639            // CREATE TABLE with CTE
1640            ("ctas_with_cte",
1641             b"CREATE TABLE final_output AS \
1642               WITH stg AS (SELECT * FROM staging_table) \
1643               SELECT * FROM stg JOIN lookup ON stg.key = lookup.key;",
1644             &["staging_table", "stg", "lookup"],
1645             &[("::stg", "::staging_table")]),
1646
1647            // INSERT with subquery + JOIN
1648            ("insert_subquery_join",
1649             b"INSERT INTO target_table \
1650               SELECT a.*, b.score FROM \
1651               (SELECT * FROM base_facts) a \
1652               JOIN scoring_model b ON a.id = b.id;",
1653             &["base_facts", "scoring_model"],
1654             &[]),
1655
1656            // Multiple DML in one file: CREATE + INSERT + SELECT
1657            ("multi_dml",
1658             b"CREATE TABLE dim_users AS SELECT * FROM raw_users; \
1659               INSERT INTO fact_orders SELECT * FROM staging_orders JOIN dim_users ON so.uid = du.uid; \
1660               SELECT * FROM fact_orders WHERE dt = '2024-01-01';",
1661             &["raw_users", "staging_orders", "dim_users", "fact_orders"],
1662             &[("::dim_users", "::raw_users")]),
1663
1664            // Correlated subquery in WHERE
1665            ("correlated_subquery",
1666             b"SELECT * FROM orders o WHERE EXISTS (SELECT 1 FROM returns r WHERE r.order_id = o.id);",
1667             &["orders", "returns"], &[]),
1668
1669            // UNION inside CTE
1670            ("cte_union",
1671             b"WITH combined AS ( \
1672                 SELECT id, amount FROM source_a \
1673                 UNION ALL \
1674                 SELECT id, amount FROM source_b \
1675               ) SELECT * FROM combined;",
1676             &["source_a", "source_b", "combined"],
1677             &[]),
1678
1679            // Deeply nested: CTE -> subquery -> JOIN
1680            ("deep_nesting",
1681             b"WITH prep AS ( \
1682                 SELECT * FROM ( \
1683                     SELECT a.*, b.cat FROM raw_events a \
1684                     LEFT JOIN categories b ON a.cat_id = b.id \
1685                 ) sub WHERE sub.cat IS NOT NULL \
1686               ) \
1687               INSERT INTO clean_events SELECT * FROM prep;",
1688             &["raw_events", "categories", "prep"],
1689             &[("::prep", "::raw_events"), ("::prep", "::categories")]),
1690
1691            // Multiple JOINs
1692            ("multi_join_4way",
1693             b"SELECT * FROM t1 \
1694               JOIN t2 ON t1.id = t2.id \
1695               LEFT JOIN t3 ON t2.id = t3.id \
1696               INNER JOIN t4 ON t3.id = t4.id;",
1697             &["t1", "t2", "t3", "t4"], &[]),
1698
1699            // INSERT with multiple JOINs
1700            ("insert_multi_join",
1701             b"INSERT INTO report \
1702               SELECT f.*, d.name, p.category FROM fact_sales f \
1703               JOIN dim_date d ON f.date_id = d.id \
1704               JOIN dim_product p ON f.prod_id = p.id;",
1705             &["fact_sales", "dim_date", "dim_product"],
1706             &[("::report", "::fact_sales")]),
1707        ];
1708
1709        let mut failures = Vec::new();
1710
1711        for (label, sql, expected_targets, expected_edges) in &cases {
1712            let extraction = infigraph_core::extract::extract_file("test.sql", sql, &pack).unwrap();
1713            let all_targets: Vec<String> = extraction
1714                .relations
1715                .iter()
1716                .map(|r| r.target_id.clone())
1717                .collect();
1718            let all_edges: Vec<String> = extraction
1719                .relations
1720                .iter()
1721                .map(|r| format!("{} -> {}", r.source_id, r.target_id))
1722                .collect();
1723
1724            for tgt in *expected_targets {
1725                let suffix = format!("::{}", tgt);
1726                if !extraction
1727                    .relations
1728                    .iter()
1729                    .any(|r| r.target_id.ends_with(&suffix))
1730                {
1731                    failures.push(format!(
1732                        "{}: missing target ref to '{}', targets: {:?}",
1733                        label, tgt, all_targets
1734                    ));
1735                }
1736            }
1737
1738            for (src_suf, tgt_suf) in *expected_edges {
1739                if !extraction
1740                    .relations
1741                    .iter()
1742                    .any(|r| r.source_id.ends_with(src_suf) && r.target_id.ends_with(tgt_suf))
1743                {
1744                    failures.push(format!(
1745                        "{}: missing edge {} -> {}, edges: {:?}",
1746                        label, src_suf, tgt_suf, all_edges
1747                    ));
1748                }
1749            }
1750        }
1751
1752        if !failures.is_empty() {
1753            panic!("Complex nested/DML failures:\n{}", failures.join("\n"));
1754        }
1755    }
1756
1757    #[test]
1758    fn test_pascal_relations() {
1759        let pack = pascal_pack().expect("Pascal pack should load");
1760        let src = br#"unit Test;
1761interface
1762uses SysUtils, Classes;
1763type
1764  TMyClass = class(TObject)
1765    procedure DoStuff;
1766  end;
1767implementation
1768procedure TMyClass.DoStuff;
1769begin
1770  WriteLn('hello');
1771  SomeObj.Method(42);
1772  inherited Create;
1773end;
1774end."#;
1775
1776        let extraction = infigraph_core::extract::extract_file(
1777            "test.pas",
1778            &src[..],
1779            &pack,
1780        ).expect("extract_file should succeed for Pascal");
1781
1782        let calls: Vec<_> = extraction.relations.iter()
1783            .filter(|r| r.kind == infigraph_core::model::RelationKind::Calls)
1784            .collect();
1785
1786        let imports: Vec<_> = extraction.relations.iter()
1787            .filter(|r| r.kind == infigraph_core::model::RelationKind::Imports)
1788            .collect();
1789
1790        let inherits: Vec<_> = extraction.relations.iter()
1791            .filter(|r| r.kind == infigraph_core::model::RelationKind::Inherits)
1792            .collect();
1793
1794        assert!(!calls.is_empty(), "Expected CALLS relations from Pascal code");
1795        assert!(!imports.is_empty(), "Expected IMPORTS relations from Pascal code");
1796        assert!(!inherits.is_empty(), "Expected INHERITS relations from Pascal code");
1797    }
1798
1799    #[test]
1800    fn test_csharp_inheritance() {
1801        let pack = csharp_pack().expect("C# pack should load");
1802        let src = br#"using System;
1803using System.Collections.Generic;
1804
1805namespace Intuit.Applications.PTG.DatabaseService
1806{
1807    public class SqliteDataLayer : IDataLayer
1808    {
1809        public void AddNewClient(string name) { }
1810        public void DeleteClient(int id) { }
1811    }
1812}
1813"#;
1814        let extraction = infigraph_core::extract::extract_file("test.cs", &src[..], &pack)
1815            .expect("extract_file should succeed for C#");
1816
1817        let inherits: Vec<_> = extraction.relations.iter()
1818            .filter(|r| r.kind == infigraph_core::model::RelationKind::Inherits)
1819            .collect();
1820        assert!(!inherits.is_empty(), "Expected INHERITS relations from C# class : interface");
1821    }
1822
1823    #[test]
1824    fn test_rust_test_extraction() {
1825        let pack = rust_pack().unwrap();
1826        let src = br#"
1827#[test]
1828fn test_add() {
1829    assert_eq!(1 + 1, 2);
1830}
1831
1832pub fn public_fn() -> i32 {
1833    42
1834}
1835
1836fn private_fn() {}
1837
1838pub(crate) fn crate_fn() {}
1839"#;
1840        let extraction = infigraph_core::extract::extract_file("lib.rs", src, &pack).unwrap();
1841        let syms = &extraction.symbols;
1842
1843        let test_add = syms.iter().find(|s| s.name == "test_add");
1844        assert!(test_add.is_some(), "test_add should be extracted");
1845        assert_eq!(test_add.unwrap().kind, infigraph_core::model::SymbolKind::Test,
1846            "test_add should have Test kind, got {:?}", test_add.unwrap().kind);
1847
1848        let public_fn = syms.iter().find(|s| s.name == "public_fn");
1849        assert!(public_fn.is_some(), "public_fn should be extracted");
1850        assert_eq!(public_fn.unwrap().visibility.as_deref(), Some("pub"),
1851            "public_fn should have pub visibility, got {:?}", public_fn.unwrap().visibility);
1852
1853        let private_fn = syms.iter().find(|s| s.name == "private_fn");
1854        assert!(private_fn.is_some(), "private_fn should be extracted");
1855        assert!(private_fn.unwrap().visibility.is_none(),
1856            "private_fn should have no visibility modifier");
1857
1858        let crate_fn = syms.iter().find(|s| s.name == "crate_fn");
1859        assert!(crate_fn.is_some(), "crate_fn should be extracted");
1860        assert_eq!(crate_fn.unwrap().visibility.as_deref(), Some("pub(crate)"),
1861            "crate_fn should have pub(crate) visibility, got {:?}", crate_fn.unwrap().visibility);
1862    }
1863
1864    #[test]
1865    fn test_rust_tokio_test_extraction() {
1866        let pack = rust_pack().unwrap();
1867        let src = br#"
1868#[tokio::test]
1869async fn test_async_op() {
1870    let x = 1;
1871}
1872"#;
1873        let extraction = infigraph_core::extract::extract_file("lib.rs", src, &pack).unwrap();
1874        let test_fn = extraction.symbols.iter().find(|s| s.name == "test_async_op");
1875        assert!(test_fn.is_some(), "test_async_op should be extracted");
1876        assert_eq!(test_fn.unwrap().kind, infigraph_core::model::SymbolKind::Test,
1877            "tokio::test should produce Test kind, got {:?}", test_fn.unwrap().kind);
1878    }
1879}