Skip to main content

gitcortex_core/
schema.rs

1use serde::{Deserialize, Serialize};
2
3/// Bumped whenever the on-disk graph schema changes.
4/// Stores compare this against the persisted version and re-index on mismatch.
5// v14 rebuilds stores created by early v13 binaries whose node/edge tables
6// predate the extended metadata and edge-line columns.
7pub const SCHEMA_VERSION: u32 = 14;
8
9/// Every named, referenceable syntactic entity becomes a node of one of these kinds.
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[serde(rename_all = "snake_case")]
12pub enum NodeKind {
13    Folder,
14    File,
15    Module,
16    Struct,
17    Enum,
18    /// Rust trait. Languages with a separate notion of interface use [`NodeKind::Interface`].
19    Trait,
20    /// Language interface (Java, TypeScript, Go) — semantically distinct from Rust traits.
21    Interface,
22    TypeAlias,
23    Function,
24    Method,
25    /// Property (Python `@property`, TypeScript `readonly` field, getter/setter pair).
26    Property,
27    Constant,
28    Macro,
29    /// Decorator / annotation declaration (e.g. `@dataclass`, `@Override`, `#[derive(...)]`).
30    Annotation,
31    /// Member of an enum (`Color::Red`, `Direction.NORTH`).
32    EnumMember,
33    /// A Markdown heading section (`## Installation`). File-level prose lives
34    /// directly on the synthesized `File` node; this kind only covers headings.
35    Section,
36    /// An HTTP route registered by a web framework (Express `app.get(...)`,
37    /// FastAPI `@app.get(...)`, etc). `name` holds `"METHOD /path"`.
38    Route,
39}
40
41impl std::fmt::Display for NodeKind {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        let s = match self {
44            NodeKind::Folder => "folder",
45            NodeKind::File => "file",
46            NodeKind::Module => "module",
47            NodeKind::Struct => "struct",
48            NodeKind::Enum => "enum",
49            NodeKind::Trait => "trait",
50            NodeKind::Interface => "interface",
51            NodeKind::TypeAlias => "type_alias",
52            NodeKind::Function => "function",
53            NodeKind::Method => "method",
54            NodeKind::Property => "property",
55            NodeKind::Constant => "constant",
56            NodeKind::Macro => "macro",
57            NodeKind::Annotation => "annotation",
58            NodeKind::EnumMember => "enum_member",
59            NodeKind::Section => "section",
60            NodeKind::Route => "route",
61        };
62        f.write_str(s)
63    }
64}
65
66impl std::str::FromStr for NodeKind {
67    type Err = ();
68    fn from_str(s: &str) -> Result<Self, Self::Err> {
69        match s {
70            "folder" => Ok(NodeKind::Folder),
71            "file" => Ok(NodeKind::File),
72            "module" => Ok(NodeKind::Module),
73            "struct" => Ok(NodeKind::Struct),
74            "enum" => Ok(NodeKind::Enum),
75            "trait" => Ok(NodeKind::Trait),
76            "interface" => Ok(NodeKind::Interface),
77            "type_alias" => Ok(NodeKind::TypeAlias),
78            "function" => Ok(NodeKind::Function),
79            "method" => Ok(NodeKind::Method),
80            "property" => Ok(NodeKind::Property),
81            "constant" => Ok(NodeKind::Constant),
82            "macro" => Ok(NodeKind::Macro),
83            "annotation" => Ok(NodeKind::Annotation),
84            "enum_member" | "enum-member" => Ok(NodeKind::EnumMember),
85            "section" => Ok(NodeKind::Section),
86            "route" => Ok(NodeKind::Route),
87            _ => Err(()),
88        }
89    }
90}
91
92/// Directed relationship between two nodes.
93#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
94#[serde(rename_all = "snake_case")]
95pub enum EdgeKind {
96    /// Parent–child containment: File→Module, Module→Struct, Struct→Method.
97    Contains,
98    /// Resolved call site: Function→Function or Method→Method.
99    Calls,
100    /// `impl Trait for Struct`, `class Foo implements Bar` — Struct→Trait/Interface.
101    Implements,
102    /// `class Foo extends Bar`, embedded struct in Go — subtype→supertype.
103    /// Distinct from `Implements`: this is "is-a" inheritance vs "can-do" interface
104    /// satisfaction.
105    Inherits,
106    /// A type appears as a parameter or return type: fn→Struct/Trait.
107    Uses,
108    /// `use path::to::Thing` import.
109    Imports,
110    /// A symbol is decorated/annotated by another (`@Override`, `@dataclass`,
111    /// `#[derive(Debug)]`).
112    Annotated,
113    /// Java `throws ExceptionType` — method→exception class.
114    Throws,
115    /// A Markdown section (or file-level prose) mentions a code symbol, via
116    /// an inline code-span or link text matching a known identifier.
117    /// Source can be cross-language by design (docs reference any language).
118    References,
119    /// A route is dispatched to its handler function: Route→Function/Method.
120    /// Directional like `Calls`/`Implements` (source is the "declaration",
121    /// target is what satisfies it).
122    HandledBy,
123}
124
125impl std::fmt::Display for EdgeKind {
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        let s = match self {
128            EdgeKind::Contains => "contains",
129            EdgeKind::Calls => "calls",
130            EdgeKind::Implements => "implements",
131            EdgeKind::Inherits => "inherits",
132            EdgeKind::Uses => "uses",
133            EdgeKind::Imports => "imports",
134            EdgeKind::Annotated => "annotated",
135            EdgeKind::Throws => "throws",
136            EdgeKind::References => "references",
137            EdgeKind::HandledBy => "handled_by",
138        };
139        f.write_str(s)
140    }
141}
142
143/// How confident the indexer is that an edge is real.
144///
145/// Ordered from highest to lowest confidence:
146/// - `Extracted` — same-file, directly observed
147/// - `Resolved`  — cross-file, import-verified (callee module was explicitly imported)
148/// - `Inferred`  — cross-file, name-match only (any same-named symbol could match)
149#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
150#[serde(rename_all = "snake_case")]
151pub enum EdgeConfidence {
152    /// Directly observed in the source (same-file resolution). Highest confidence.
153    #[default]
154    Extracted,
155    /// Cross-file edge where the callee's module was explicitly imported at the call site.
156    /// Higher confidence than `Inferred` — false-positive rate is far lower.
157    Resolved,
158    /// Resolved cross-file by unqualified name match only. Lowest confidence.
159    Inferred,
160}
161
162impl std::fmt::Display for EdgeConfidence {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        f.write_str(match self {
165            EdgeConfidence::Extracted => "extracted",
166            EdgeConfidence::Resolved => "resolved",
167            EdgeConfidence::Inferred => "inferred",
168        })
169    }
170}
171
172impl EdgeConfidence {
173    /// Parse from the stored string form; unknown/empty defaults to `Extracted`.
174    pub fn from_label(s: &str) -> Self {
175        match s {
176            "resolved" => EdgeConfidence::Resolved,
177            "inferred" => EdgeConfidence::Inferred,
178            _ => EdgeConfidence::Extracted,
179        }
180    }
181}
182
183/// Symbol visibility in the source language.
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
185#[serde(rename_all = "snake_case")]
186pub enum Visibility {
187    #[default]
188    Private,
189    PubCrate,
190    Pub,
191}
192
193impl std::fmt::Display for Visibility {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        match self {
196            Visibility::Pub => f.write_str("pub"),
197            Visibility::PubCrate => f.write_str("pub_crate"),
198            Visibility::Private => f.write_str("private"),
199        }
200    }
201}
202
203impl std::str::FromStr for Visibility {
204    type Err = ();
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        match s {
207            "pub" => Ok(Visibility::Pub),
208            "pub_crate" => Ok(Visibility::PubCrate),
209            "private" => Ok(Visibility::Private),
210            _ => Err(()),
211        }
212    }
213}
214
215// ── LLD labels ──────────────────────────────────────────────────────────────
216
217/// Which SOLID principle a node may be violating (populated in pass 2).
218#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
219#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
220pub enum SolidHint {
221    /// Too many responsibilities in one type.
222    Srp,
223    /// Logic closed for extension but open for modification.
224    Ocp,
225    /// Subtype breaks contract of supertype.
226    Lsp,
227    /// Interface has too many unrelated methods.
228    Isp,
229    /// Depends on concrete type instead of abstraction.
230    Dip,
231}
232
233/// Common design patterns detectable syntactically.
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235#[serde(rename_all = "snake_case")]
236pub enum DesignPattern {
237    Builder,
238    Factory,
239    Observer,
240    Strategy,
241    Decorator,
242    Singleton,
243    Repository,
244}
245
246/// Code quality smells detectable without full type resolution.
247#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
248#[serde(rename_all = "snake_case")]
249pub enum CodeSmell {
250    /// Struct with too many methods or dependencies.
251    GodStruct,
252    /// Function body too long.
253    LongMethod,
254    /// Nesting depth exceeds threshold.
255    DeepNesting,
256    /// Trait with too many methods.
257    FatInterface,
258}