mati_core/analysis/parser/import.rs
1//! Structured import representation for cross-file resolution.
2//!
3//! Raw import strings (`Vec<String>`) lack the structural information needed
4//! for production-grade import resolution across 12 languages. This module
5//! provides `ImportStatement` — a typed representation that carries the import
6//! path, its structural classification, and source location.
7//!
8//! Each language parser produces `Vec<ImportStatement>` during tree-sitter
9//! extraction. The classification into `ImportKind` happens at parse time,
10//! eliminating the need for a separate `is_external_import()` pass during
11//! edge construction.
12
13use serde::{Deserialize, Serialize};
14
15/// A single import statement extracted from source code by tree-sitter.
16///
17/// Carries enough information for the resolver to decide whether to attempt
18/// resolution, and for debugging/IDE integration via the line number.
19#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct ImportStatement {
21 /// The raw path/module string as extracted from source.
22 ///
23 /// - **Rust**: the `use` argument without the `as` alias (e.g. `crate::store::db`)
24 /// - **Python**: the dotted module name (e.g. `django.conf` or `.helpers`)
25 /// - **TypeScript/JavaScript**: the module specifier without quotes (e.g. `./utils` or `react`)
26 /// - **Go**: the import path without quotes (e.g. `fmt` or `github.com/user/pkg`)
27 /// - **Java**: the fully-qualified class/package name (e.g. `java.util.List`)
28 /// - **C/C++**: the filename inside the include directive (e.g. `stdio.h` or `myheader.h`)
29 /// - **Ruby**: the argument to `require`/`require_relative`
30 /// - **Scala**: the import path without `import ` prefix
31 /// - **Elixir**: the module name from `import`/`alias`/`use`/`require`
32 /// - **Haskell**: the module name (e.g. `Data.List`)
33 pub path: String,
34
35 /// Structural classification that determines resolver behavior.
36 pub kind: ImportKind,
37
38 /// Line number in the source file where the import appears (1-indexed).
39 /// Used for debugging and future IDE integration. 0 if unknown.
40 pub line: u32,
41}
42
43/// Classification of an import statement that determines how the resolver
44/// handles it.
45///
46/// An import can only have one kind. Priority when multiple could apply:
47/// `External` > `Relative` > `Wildcard` > `Normal`. External imports are
48/// never resolved; relative imports use the importing file's directory as
49/// base; wildcards may match multiple targets in future phases.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
51pub enum ImportKind {
52 /// Standard import (e.g. `use foo::bar;`, `import X`, `#include "foo.h"`).
53 /// The resolver will attempt to resolve this against the file index.
54 Normal,
55
56 /// Wildcard / glob import (e.g. `use foo::*;`, `from x import *`, `import com.acme.*`).
57 /// The resolver will attempt resolution but may match multiple targets.
58 Wildcard,
59
60 /// Relative import that resolves from the importing file's directory.
61 /// Used by Python (`.x`, `..x`), Ruby (`require_relative`), C (`#include "..."`),
62 /// and TS/JS (`./foo`, `../bar`).
63 Relative,
64
65 /// External / third-party import that should be skipped by the resolver.
66 /// (e.g. Rust non-crate imports, TS/JS bare specifiers, C angle-bracket system headers)
67 External,
68
69 /// Class inheritance: `class Foo < Bar` produces an Inherits import for Bar.
70 /// Used by Ruby/Rails for autoload-implicit dependencies resolved via Zeitwerk
71 /// path conventions.
72 Inherits,
73
74 /// Module inclusion: `include Foo`, `extend Foo`, `prepend Foo` all produce
75 /// an Includes import. Used by Ruby/Rails for concerns and shared modules,
76 /// resolved via Zeitwerk path conventions.
77 Includes,
78}
79
80impl ImportStatement {
81 /// Create a new import statement with all fields specified.
82 pub fn new(path: impl Into<String>, kind: ImportKind, line: u32) -> Self {
83 Self {
84 path: path.into(),
85 kind,
86 line,
87 }
88 }
89
90 /// Convenience: create a Normal import at the given line.
91 #[cfg(test)]
92 pub fn normal(path: impl Into<String>, line: u32) -> Self {
93 Self::new(path, ImportKind::Normal, line)
94 }
95
96 /// Convenience: create an External import at the given line.
97 #[cfg(test)]
98 pub fn external(path: impl Into<String>, line: u32) -> Self {
99 Self::new(path, ImportKind::External, line)
100 }
101}