codingest 0.2.6

codingest — standalone extraction of KGLite's code-tree component: parse polyglot codebases into kglite knowledge graphs (tree-sitter parsers, call/type/route edges, docs pass, multi-rev merge)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
//! Data models for parsed code entities (ported from parsers/models.py).
//!
//! Field order is preserved from the Python dataclasses so that any
//! downstream JSON (e.g. the `fields` blob embedded on Class/Struct nodes,
//! consumed by the MCP code-review server) remains byte-identical.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

pub type MetadataMap = HashMap<String, serde_json::Value>;

/// An annotation extracted from a comment (TODO/FIXME/HACK/SAFETY/XXX/BUG/NOTE/WARNING).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Annotation {
    pub kind: String,
    pub text: String,
    pub line: u32,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FileInfo {
    /// Path relative to `src_root`.
    pub path: String,
    pub filename: String,
    pub loc: u32,
    /// Language-specific qualified path (e.g. `foo::bar::baz`, `foo.bar.baz`).
    pub module_path: String,
    /// Canonical identifier from `parsers::registry::LANGUAGES`.
    pub language: String,
    pub submodule_declarations: Vec<String>,
    pub imports: Vec<String>,
    /// Exported names (`__all__`, `pub` items).
    pub exports: Vec<String>,
    /// TODO/FIXME/HACK/SAFETY comments. `None` if none were found.
    pub annotations: Option<Vec<Annotation>>,
    pub is_test: bool,
    /// Set when ingestion deliberately skipped the file. Values:
    /// `"generated"` (auto-generated stubs/codegen), `"minified"` (single-line bundles
    /// or extreme line widths). Skipped files emit no Function/Class/Constant nodes —
    /// only this FileInfo so users can audit what was filtered.
    pub skip_reason: Option<String>,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct FunctionInfo {
    pub name: String,
    pub qualified_name: String,
    pub visibility: String,
    pub is_async: bool,
    pub is_method: bool,
    pub signature: String,
    pub file_path: String,
    pub line_number: u32,
    pub docstring: Option<String>,
    pub return_type: Option<String>,
    pub decorators: Vec<String>,
    /// `(callee_name, line_number)` pairs.
    pub calls: Vec<(String, u32)>,
    /// Identifier references in the function body that *look like
    /// constants* (terminal segment matches `SCREAMING_SNAKE_CASE`).
    /// The builder resolves these against the project's `ConstantInfo`
    /// set to emit `REFERENCES` edges from Function → Constant.
    /// Stored as `(identifier_text, line_number)` pairs.
    pub references: Vec<(String, u32)>,
    /// Function-pointer references: bare or scoped identifiers passed
    /// as arguments to a higher-order function (e.g.
    /// `iter.and_then(some_fn)`). Distinct from `calls` because the
    /// referenced function isn't necessarily invoked at the reference
    /// site — the value is passed by reference. The builder resolves
    /// these against the project's function set to emit
    /// `REFERENCES_FN` edges. Stored as `(identifier_text, line)` pairs.
    pub function_refs: Vec<(String, u32)>,
    /// Generic/template parameters, e.g. `"T, U: Display"`.
    pub type_parameters: Option<String>,
    pub end_line: Option<u32>,
    /// Structured parameter list — one entry per declared parameter (excluding
    /// `self`/`cls`/`&self`/`&mut self`). Populated by per-language parsers; the
    /// `signature` string remains the user-facing source of truth, this field is
    /// for graph builders that want structured access (e.g. USES_TYPE on params).
    pub parameters: Vec<ParameterInfo>,
    /// Cyclomatic-style branch count over the function body. Counts each
    /// `if`/`elif`/`while`/`for`/`case`/`catch`/ternary/`&&`/`||`-style node the
    /// language grammar exposes. `None` if the parser didn't compute it (e.g. the
    /// function had no body — abstract/protocol methods, `.pyi` stubs).
    pub branch_count: Option<u32>,
    /// Parameter count (excluding `self`/`cls`/`&self`/`&mut self`).
    pub param_count: Option<u32>,
    /// Max nesting depth of branch nodes encountered while walking the body.
    pub max_nesting: Option<u32>,
    /// `Some(true)` if the function body contains a bare-name call resolving to
    /// the function's own short name. Heuristic — refined later by the resolved
    /// CALLS edges.
    pub is_recursive: Option<bool>,
    /// Set when the function's docstring or leading comment contains one or
    /// more `@procedure: NAME` annotations. A single function may register
    /// under multiple procedure names (aliases). The builder synthesizes one
    /// `Procedure` node per name and emits a `Procedure -[IMPLEMENTED_BY]->
    /// Function` edge for each, letting graph-of-procedures use cases
    /// (Cypher CALL registry, RPC method catalogs, command-bus dispatchers)
    /// sit on top of the code graph.
    pub procedure_names: Vec<String>,
    /// Flexible language-specific flags (is_pymethod, is_test, ffi_kind, py_name, ...).
    pub metadata: MetadataMap,
}

/// One declared parameter on a function/method.
///
/// Excludes implicit receivers (`self`/`cls`/`&self`/`&mut self`). The parser
/// emits `ParameterKind::Variadic` for `*args`/spread/`...rest`,
/// `ParameterKind::KwVariadic` for Python `**kwargs`, and `ParameterKind::Positional`
/// for everything else. `default` carries the raw default-expression text when present.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ParameterInfo {
    pub name: String,
    pub type_annotation: Option<String>,
    pub default: Option<String>,
    pub kind: ParameterKind,
}

#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ParameterKind {
    #[default]
    Positional,
    Variadic,
    KwVariadic,
    /// Method receiver — Go's `(c *Call)`, Rust's `&self`/`&mut self`/`self`.
    /// Stored in `parameters` so the USES_TYPE resolver can attribute the
    /// receiver type with `position="receiver"`. Excluded from `param_count`
    /// since receivers aren't user-supplied arguments.
    Receiver,
}

/// Represents structs (Rust/C++), classes (Python/TS/Java/C#).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ClassInfo {
    pub name: String,
    pub qualified_name: String,
    /// "struct" | "class" — determines graph node type.
    pub kind: String,
    pub visibility: String,
    pub file_path: String,
    pub line_number: u32,
    pub docstring: Option<String>,
    pub bases: Vec<String>,
    pub type_parameters: Option<String>,
    pub end_line: Option<u32>,
    pub metadata: MetadataMap,
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EnumInfo {
    pub name: String,
    pub qualified_name: String,
    pub visibility: String,
    pub file_path: String,
    pub line_number: u32,
    pub docstring: Option<String>,
    pub variants: Vec<String>,
    pub end_line: Option<u32>,
    /// Structured variant info per language (C++ enum values, Rust tuple/struct variants).
    pub variant_details: Option<Vec<serde_json::Value>>,
}

/// Represents traits (Rust), protocols (Python), interfaces (TS/Java/C#).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InterfaceInfo {
    pub name: String,
    pub qualified_name: String,
    /// "trait" | "protocol" | "interface"
    pub kind: String,
    pub visibility: String,
    pub file_path: String,
    pub line_number: u32,
    pub docstring: Option<String>,
    pub type_parameters: Option<String>,
    pub end_line: Option<u32>,
}

/// Class/struct field or property.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AttributeInfo {
    pub name: String,
    /// Fully-qualified name, e.g. `"chromadb.Collection.name"`.
    pub qualified_name: String,
    /// Owner's qualified name, e.g. `"chromadb.Collection"`.
    pub owner_qualified_name: String,
    pub type_annotation: Option<String>,
    pub visibility: String,
    pub file_path: String,
    pub line_number: u32,
    pub default_value: Option<String>,
}

/// Top-level constant, type alias, or static variable.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConstantInfo {
    pub name: String,
    pub qualified_name: String,
    /// "constant" | "type_alias" | "static"
    pub kind: String,
    pub type_annotation: Option<String>,
    /// First ~100 chars of the value expression.
    pub value_preview: Option<String>,
    pub visibility: String,
    pub file_path: String,
    pub line_number: u32,
}

/// A semantically-interesting HTML / template element — heading
/// (`h1`–`h6`), element with an `id` attribute, or a `<form>` with an
/// `action` attribute. The HTML parser (0.9.36) emits one per element
/// that fits one of those three shapes; everything else stays as parse
/// noise to keep god-HTML-file graphs navigable.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ElementInfo {
    /// Short display name. For headings, the text content (truncated to
    /// 100 chars). For sections, the `id` attribute value. For forms,
    /// the `action` string (or `"form_<n>"` when action is absent).
    pub name: String,
    /// Stable qualified name — `"{rel_path}:{tag}:{anchor}"`.
    pub qualified_name: String,
    /// HTML tag name (`h1`/`h2`/.../`section`/`main`/`header`/`form`/...).
    pub tag: String,
    /// `"heading"` | `"section"` | `"form"`.
    pub kind: String,
    /// `id` attribute value, when present.
    pub id: Option<String>,
    /// Form `action` attribute.
    pub action: Option<String>,
    /// Form `method` attribute (defaults to `"GET"` when absent in HTML
    /// but we record `None` to preserve "wasn't declared" vs
    /// "declared empty").
    pub method: Option<String>,
    pub file_path: String,
    pub line_number: u32,
    pub end_line: Option<u32>,
    /// Drives `Element -[CONTAINS]-> Element` edges. The qualified_name
    /// of the nearest emitted ancestor (heading/section/form). `None`
    /// for top-level elements directly under `<body>`.
    pub parent_qname: Option<String>,
}

/// A CSS rule_set selector. The 0.9.36 CSS parser emits one per
/// `rule_set`, regardless of the rule's selector-list count (a rule
/// `.foo, .bar, .baz { ... }` emits one node named `.foo, .bar, .baz`,
/// not three). Custom-property declarations are emitted separately as
/// `ConstantInfo` rows.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SelectorInfo {
    /// Canonicalised selector-list string (e.g. `".foo, .bar"`).
    pub name: String,
    /// Stable qualified name — `"{rel_path}:{line}:{selector}"`.
    pub qualified_name: String,
    /// Always `"rule"` in 0.9.36; reserved for future
    /// `"keyframes"`/`"media"`/etc. extensions.
    pub kind: String,
    pub file_path: String,
    pub line_number: u32,
    pub end_line: Option<u32>,
}

/// An impl block (Rust), inheritance (Python/Java/C#), or implements (TS).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct TypeRelationship {
    pub source_type: String,
    /// `None` for inherent impls.
    pub target_type: Option<String>,
    /// "implements" | "extends" | "inherent"
    pub relationship: String,
    pub methods: Vec<FunctionInfo>,
}

/// Semantic category of a parsed control-transfer site.
///
/// Parsers that can distinguish returning calls from jumps and conditional
/// branches record sites here instead of flattening every transfer into
/// [`FunctionInfo::calls`]. Indirect variants deliberately carry no target
/// edge: the destination is computed at runtime and fabricating one would be
/// worse than recording an unresolved site.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ControlTransferKind {
    Call,
    Jump,
    Branch,
    IndirectCall,
    IndirectJump,
}

/// One source-level control transfer with optional architecture-specific
/// mechanics preserved as ordinary values.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ControlTransferInfo {
    pub caller: String,
    /// Exact qualified target after parser-level namespace resolution.
    /// `None` for register/relative/otherwise-indirect transfers.
    pub target: Option<String>,
    pub kind: ControlTransferKind,
    pub line: u32,
    /// Original operand spelling before normalization.
    pub raw_operand: String,
    /// Signed source offset spelling (for example `+2`), when present.
    pub offset: Option<String>,
    /// Transfer mechanism such as an assembly trampoline name.
    pub via: Option<String>,
    /// Address-word line consumed by a multi-line transfer form.
    pub address_line: Option<u32>,
}

/// Conservative data-access classification for a source reference site.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReferenceAccess {
    Read,
    Write,
    ReadWrite,
    Address,
    Unknown,
}

/// One source-level reference to an exact qualified Constant target.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ReferenceSiteInfo {
    pub caller: String,
    pub target: String,
    pub line: u32,
    pub opcode: String,
    pub access: ReferenceAccess,
}

/// Relationship emitted from a named symbol definition.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SymbolRelationshipKind {
    AliasOf,
    PointsTo,
}

/// Graph node type of a resolved symbol-link target.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum SymbolTargetKind {
    Constant,
    Function,
}

/// Exact relationship from a Constant symbol definition to another symbol.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct SymbolRelationshipInfo {
    pub source: String,
    pub target: String,
    pub target_kind: SymbolTargetKind,
    pub relationship: SymbolRelationshipKind,
    pub line: u32,
    pub raw_target: String,
}

/// Unified result of parsing one or more files.
#[derive(Debug, Clone, Default)]
pub struct ParseResult {
    pub files: Vec<FileInfo>,
    pub functions: Vec<FunctionInfo>,
    pub classes: Vec<ClassInfo>,
    pub enums: Vec<EnumInfo>,
    pub interfaces: Vec<InterfaceInfo>,
    pub type_relationships: Vec<TypeRelationship>,
    pub attributes: Vec<AttributeInfo>,
    pub constants: Vec<ConstantInfo>,
    pub elements: Vec<ElementInfo>,
    pub selectors: Vec<SelectorInfo>,
    pub control_transfers: Vec<ControlTransferInfo>,
    pub reference_sites: Vec<ReferenceSiteInfo>,
    pub symbol_relationships: Vec<SymbolRelationshipInfo>,
}

impl ParseResult {
    pub fn new() -> Self {
        Self::default()
    }

    /// Move the contents of `other` into `self`.
    pub fn merge(&mut self, mut other: ParseResult) {
        self.files.append(&mut other.files);
        self.functions.append(&mut other.functions);
        self.classes.append(&mut other.classes);
        self.enums.append(&mut other.enums);
        self.interfaces.append(&mut other.interfaces);
        self.type_relationships
            .append(&mut other.type_relationships);
        self.attributes.append(&mut other.attributes);
        self.constants.append(&mut other.constants);
        self.elements.append(&mut other.elements);
        self.selectors.append(&mut other.selectors);
        self.control_transfers.append(&mut other.control_transfers);
        self.reference_sites.append(&mut other.reference_sites);
        self.symbol_relationships
            .append(&mut other.symbol_relationships);
    }
}

// ── Manifest / project-level models ────────────────────────────────

/// A directory containing source code to parse.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SourceRoot {
    /// Absolute path to the directory.
    pub path: PathBuf,
    /// `None` = auto-detect language.
    pub language: Option<String>,
    pub is_test: bool,
    /// e.g. `"python-package"`, `"rust-crate"`.
    pub label: Option<String>,
}

/// A project dependency declared in a manifest.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct DependencyInfo {
    pub name: String,
    pub version_spec: Option<String>,
    pub is_dev: bool,
    pub is_optional: bool,
    /// Optional dep group name.
    pub group: Option<String>,
}

/// Sentinel written to the `:Project` node's `manifest` property when no
/// supported manifest was found and the project was *inferred* from the
/// directory being built (name = the project root's own directory name,
/// languages = the languages actually parsed).
///
/// It deliberately reuses the existing `manifest` column rather than adding an
/// `inferred` one: the parity sweep compares every property column of every
/// node, so a new column would move the golden of every manifest-backed graph
/// too, and the whole point of the inference is to be additive for them. The
/// parenthesised spelling cannot collide with a real manifest path (no
/// manifest is named `(inferred)`), and it reads as a value rather than an
/// absence in a query: `MATCH (p:Project) WHERE p.manifest = '(inferred)'`.
pub const INFERRED_MANIFEST: &str = "(inferred)";

/// Project metadata extracted from a manifest file — or, when no supported
/// manifest exists, inferred from the project root (see [`INFERRED_MANIFEST`]).
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProjectInfo {
    pub name: String,
    pub version: Option<String>,
    pub description: Option<String>,
    pub languages: Vec<String>,
    pub authors: Vec<String>,
    pub license: Option<String>,
    pub repository_url: Option<String>,
    pub manifest_path: String,
    pub source_roots: Vec<SourceRoot>,
    pub test_roots: Vec<SourceRoot>,
    pub dependencies: Vec<DependencyInfo>,
    /// e.g. `"maturin"`, `"setuptools"`, `"cargo"`.
    pub build_system: Option<String>,
    pub metadata: MetadataMap,
}

// ── Field-embedding JSON (for ClassInfo.fields on graph nodes) ────────
//
// `fields` is serialized as a JSON string attached to Class/Struct nodes.
// Preserve exact key order: name, type, visibility, default.
// (This is verified in tests/test_code_tree_parity.py via a direct JSON
// equality assertion on a known Class node.)

/// One entry in the JSON `fields` blob on a Class/Struct node.
#[derive(Debug, Clone, Serialize)]
pub struct FieldEntry {
    pub name: String,
    #[serde(rename = "type")]
    pub r#type: Option<String>,
    pub visibility: String,
    pub default: Option<String>,
}