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}
37
38impl std::fmt::Display for NodeKind {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        let s = match self {
41            NodeKind::Folder => "folder",
42            NodeKind::File => "file",
43            NodeKind::Module => "module",
44            NodeKind::Struct => "struct",
45            NodeKind::Enum => "enum",
46            NodeKind::Trait => "trait",
47            NodeKind::Interface => "interface",
48            NodeKind::TypeAlias => "type_alias",
49            NodeKind::Function => "function",
50            NodeKind::Method => "method",
51            NodeKind::Property => "property",
52            NodeKind::Constant => "constant",
53            NodeKind::Macro => "macro",
54            NodeKind::Annotation => "annotation",
55            NodeKind::EnumMember => "enum_member",
56            NodeKind::Section => "section",
57        };
58        f.write_str(s)
59    }
60}
61
62impl std::str::FromStr for NodeKind {
63    type Err = ();
64    fn from_str(s: &str) -> Result<Self, Self::Err> {
65        match s {
66            "folder" => Ok(NodeKind::Folder),
67            "file" => Ok(NodeKind::File),
68            "module" => Ok(NodeKind::Module),
69            "struct" => Ok(NodeKind::Struct),
70            "enum" => Ok(NodeKind::Enum),
71            "trait" => Ok(NodeKind::Trait),
72            "interface" => Ok(NodeKind::Interface),
73            "type_alias" => Ok(NodeKind::TypeAlias),
74            "function" => Ok(NodeKind::Function),
75            "method" => Ok(NodeKind::Method),
76            "property" => Ok(NodeKind::Property),
77            "constant" => Ok(NodeKind::Constant),
78            "macro" => Ok(NodeKind::Macro),
79            "annotation" => Ok(NodeKind::Annotation),
80            "enum_member" | "enum-member" => Ok(NodeKind::EnumMember),
81            "section" => Ok(NodeKind::Section),
82            _ => Err(()),
83        }
84    }
85}
86
87/// Directed relationship between two nodes.
88#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum EdgeKind {
91    /// Parent–child containment: File→Module, Module→Struct, Struct→Method.
92    Contains,
93    /// Resolved call site: Function→Function or Method→Method.
94    Calls,
95    /// `impl Trait for Struct`, `class Foo implements Bar` — Struct→Trait/Interface.
96    Implements,
97    /// `class Foo extends Bar`, embedded struct in Go — subtype→supertype.
98    /// Distinct from `Implements`: this is "is-a" inheritance vs "can-do" interface
99    /// satisfaction.
100    Inherits,
101    /// A type appears as a parameter or return type: fn→Struct/Trait.
102    Uses,
103    /// `use path::to::Thing` import.
104    Imports,
105    /// A symbol is decorated/annotated by another (`@Override`, `@dataclass`,
106    /// `#[derive(Debug)]`).
107    Annotated,
108    /// Java `throws ExceptionType` — method→exception class.
109    Throws,
110    /// A Markdown section (or file-level prose) mentions a code symbol, via
111    /// an inline code-span or link text matching a known identifier.
112    /// Source can be cross-language by design (docs reference any language).
113    References,
114}
115
116impl std::fmt::Display for EdgeKind {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        let s = match self {
119            EdgeKind::Contains => "contains",
120            EdgeKind::Calls => "calls",
121            EdgeKind::Implements => "implements",
122            EdgeKind::Inherits => "inherits",
123            EdgeKind::Uses => "uses",
124            EdgeKind::Imports => "imports",
125            EdgeKind::Annotated => "annotated",
126            EdgeKind::Throws => "throws",
127            EdgeKind::References => "references",
128        };
129        f.write_str(s)
130    }
131}
132
133/// How confident the indexer is that an edge is real.
134///
135/// Ordered from highest to lowest confidence:
136/// - `Extracted` — same-file, directly observed
137/// - `Resolved`  — cross-file, import-verified (callee module was explicitly imported)
138/// - `Inferred`  — cross-file, name-match only (any same-named symbol could match)
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
140#[serde(rename_all = "snake_case")]
141pub enum EdgeConfidence {
142    /// Directly observed in the source (same-file resolution). Highest confidence.
143    #[default]
144    Extracted,
145    /// Cross-file edge where the callee's module was explicitly imported at the call site.
146    /// Higher confidence than `Inferred` — false-positive rate is far lower.
147    Resolved,
148    /// Resolved cross-file by unqualified name match only. Lowest confidence.
149    Inferred,
150}
151
152impl std::fmt::Display for EdgeConfidence {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        f.write_str(match self {
155            EdgeConfidence::Extracted => "extracted",
156            EdgeConfidence::Resolved => "resolved",
157            EdgeConfidence::Inferred => "inferred",
158        })
159    }
160}
161
162impl EdgeConfidence {
163    /// Parse from the stored string form; unknown/empty defaults to `Extracted`.
164    pub fn from_label(s: &str) -> Self {
165        match s {
166            "resolved" => EdgeConfidence::Resolved,
167            "inferred" => EdgeConfidence::Inferred,
168            _ => EdgeConfidence::Extracted,
169        }
170    }
171}
172
173/// Symbol visibility in the source language.
174#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
175#[serde(rename_all = "snake_case")]
176pub enum Visibility {
177    #[default]
178    Private,
179    PubCrate,
180    Pub,
181}
182
183impl std::fmt::Display for Visibility {
184    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
185        match self {
186            Visibility::Pub => f.write_str("pub"),
187            Visibility::PubCrate => f.write_str("pub_crate"),
188            Visibility::Private => f.write_str("private"),
189        }
190    }
191}
192
193impl std::str::FromStr for Visibility {
194    type Err = ();
195    fn from_str(s: &str) -> Result<Self, Self::Err> {
196        match s {
197            "pub" => Ok(Visibility::Pub),
198            "pub_crate" => Ok(Visibility::PubCrate),
199            "private" => Ok(Visibility::Private),
200            _ => Err(()),
201        }
202    }
203}
204
205// ── LLD labels ──────────────────────────────────────────────────────────────
206
207/// Which SOLID principle a node may be violating (populated in pass 2).
208#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
210pub enum SolidHint {
211    /// Too many responsibilities in one type.
212    Srp,
213    /// Logic closed for extension but open for modification.
214    Ocp,
215    /// Subtype breaks contract of supertype.
216    Lsp,
217    /// Interface has too many unrelated methods.
218    Isp,
219    /// Depends on concrete type instead of abstraction.
220    Dip,
221}
222
223/// Common design patterns detectable syntactically.
224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
225#[serde(rename_all = "snake_case")]
226pub enum DesignPattern {
227    Builder,
228    Factory,
229    Observer,
230    Strategy,
231    Decorator,
232    Singleton,
233    Repository,
234}
235
236/// Code quality smells detectable without full type resolution.
237#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
238#[serde(rename_all = "snake_case")]
239pub enum CodeSmell {
240    /// Struct with too many methods or dependencies.
241    GodStruct,
242    /// Function body too long.
243    LongMethod,
244    /// Nesting depth exceeds threshold.
245    DeepNesting,
246    /// Trait with too many methods.
247    FatInterface,
248}