Skip to main content

weavatrix_parse/
facts.rs

1//! Language-neutral facts a structural pass extracts from a token stream.
2//!
3//! These are the shapes repository intelligence actually consumes. Anything a
4//! consumer cannot use - operator precedence, expression trees, type
5//! inference - is deliberately absent, which is what keeps extraction linear
6//! in the token count.
7
8/// Position of a fact in its source file.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct Span {
11    pub start: usize,
12    pub end: usize,
13    pub line: u32,
14    pub column: u32,
15    pub end_line: u32,
16    pub end_column: u32,
17}
18
19/// The GraphQL root operation a field exposes or an executable document calls.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum GraphqlOperation {
22    Query,
23    Mutation,
24    Subscription,
25}
26
27/// The schema role of a GraphQL named type.
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum GraphqlType {
30    Object,
31    Interface,
32    Input,
33    Enum,
34    Scalar,
35    Union,
36}
37
38/// A typed API-contract fact.
39#[derive(Debug, Clone, PartialEq, Eq)]
40#[non_exhaustive]
41pub enum ContractKind {
42    GraphqlType(GraphqlType),
43    GraphqlField {
44        operation: Option<GraphqlOperation>,
45        return_type: String,
46    },
47    GraphqlOperation(GraphqlOperation),
48    GraphqlCall(GraphqlOperation),
49    GraphqlFragment {
50        on_type: String,
51        operation: Option<GraphqlOperation>,
52    },
53    GraphqlFragmentSpread,
54    ProtobufPackage,
55    ProtobufMessage,
56    ProtobufEnum,
57    ProtobufService,
58    ProtobufRpc {
59        input: String,
60        output: String,
61        client_streaming: bool,
62        server_streaming: bool,
63    },
64}
65
66/// A named contract element and its exact source location.
67#[derive(Debug, Clone, PartialEq, Eq)]
68pub struct Contract {
69    pub name: String,
70    pub kind: ContractKind,
71    pub span: Span,
72    pub owner: Option<String>,
73}
74
75/// A fail-closed diagnostic emitted instead of guessed structural facts.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct ParseDiagnostic {
78    pub code: &'static str,
79    pub message: String,
80    pub span: Span,
81}
82
83/// What a declared name is.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum DeclarationKind {
87    Function,
88    Method,
89    Class,
90    Interface,
91    Enum,
92    TypeAlias,
93    Field,
94    Constant,
95    Variable,
96    Module,
97    Struct,
98    Trait,
99    Table,
100    View,
101    Procedure,
102    /// A CSS class or id selector, named with its leading `.` or `#`.
103    Selector,
104    /// An infrastructure object: a Terraform resource, data source or output.
105    Resource,
106    /// A section heading in a document.
107    Heading,
108}
109
110/// A named declaration and where it was written.
111#[derive(Debug, Clone, PartialEq, Eq)]
112#[non_exhaustive]
113pub struct Declaration {
114    pub name: String,
115    pub kind: DeclarationKind,
116    /// Exact source occupied by the declaration name and its modifiers.
117    pub span: Span,
118    /// Full source occupied by the declaration, including its body when one exists.
119    pub extent: Span,
120    /// Enclosing declaration, when the language nests them.
121    pub owner: Option<String>,
122    /// Whether the declaration leaves the module.
123    pub exported: bool,
124}
125
126/// A module this file pulls in.
127#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct ImportBinding {
129    /// The name exported by the imported module.
130    pub imported: String,
131    /// The name made available in this file.
132    pub local: String,
133}
134
135/// A module this file pulls in.
136#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct Import {
138    /// The specifier exactly as written, without quotes.
139    pub specifier: String,
140    pub span: Span,
141    /// A type-position import, which disappears when the code is compiled.
142    pub type_only: bool,
143    /// `export ... from`, which forwards another module's surface.
144    pub reexport: bool,
145    /// Local names this import binds.
146    ///
147    /// Without them a consumer meeting `router` in `app.use("/api", router)`
148    /// cannot tell which module it came from, and the mount resolves to
149    /// nothing.
150    pub names: Vec<String>,
151    /// Lossless exported-to-local binding pairs.
152    ///
153    /// `names` remains the backward-compatible list of local names. This field
154    /// preserves the source name too, so `import { original as local }` can be
155    /// resolved to `original` without a repository-wide guess for `local`.
156    pub bindings: Vec<ImportBinding>,
157}
158
159/// Why one name mentions another.
160///
161/// A call and an `extends` clause are both "this name depends on that name",
162/// and separating them into different fact types would force every consumer to
163/// walk two collections to answer one question.
164#[derive(Debug, Clone, Copy, PartialEq, Eq)]
165#[non_exhaustive]
166pub enum ReferenceKind {
167    Call,
168    Inherits,
169    Implements,
170    /// A name used without being called, as an HTML `class` attribute uses a
171    /// CSS selector.
172    Uses,
173    /// A statement that reads the named object, as `SELECT ... FROM users`.
174    Reads,
175    /// A statement that writes it, as `INSERT INTO users`.
176    Writes,
177}
178
179/// One name mentioning another.
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub struct Reference {
182    /// The referenced name, without its receiver.
183    pub name: String,
184    pub kind: ReferenceKind,
185    /// Receiver written before the final dot, when there was one.
186    pub receiver: Option<String>,
187    pub span: Span,
188    /// Enclosing declaration the reference was written in.
189    pub owner: Option<String>,
190    /// Literal string arguments, which carry routes, topics and table names.
191    pub string_arguments: Vec<String>,
192    /// Names passed as arguments, in the order written.
193    ///
194    /// `app.use("/api", router)` mounts one module under a prefix, and the
195    /// prefix is a string while the module is a name - so a consumer that
196    /// only sees literals sees half the fact and can resolve neither end.
197    pub name_arguments: Vec<String>,
198}
199
200/// Everything one structural pass found in one file.
201#[derive(Debug, Clone, Default, PartialEq, Eq)]
202pub struct Facts {
203    pub declarations: Vec<Declaration>,
204    /// Exact spans of declarations that exist only in a test compilation.
205    ///
206    /// Keeping this sparse avoids enlarging every declaration in every
207    /// language. Rust fills it from `#[test]` and positive `#[cfg(test)]`
208    /// contexts; languages without compile-time test scopes leave it empty.
209    pub test_only_declarations: Vec<Span>,
210    pub imports: Vec<Import>,
211    pub references: Vec<Reference>,
212    pub contracts: Vec<Contract>,
213    pub diagnostics: Vec<ParseDiagnostic>,
214}
215
216impl Facts {
217    /// Whether the declaration at `span` only exists in a test compilation.
218    #[must_use]
219    pub fn declaration_is_test_only(&self, span: Span) -> bool {
220        self.test_only_declarations.contains(&span)
221    }
222
223    /// Just the call sites, for consumers that want a call graph and nothing
224    /// else.
225    pub fn calls(&self) -> impl Iterator<Item = &Reference> {
226        self.references
227            .iter()
228            .filter(|reference| reference.kind == ReferenceKind::Call)
229    }
230}