tga_core/models/mod.rs
1//! Domain models corresponding to the v1 database schema.
2//!
3//! These structs are the in-memory representation of rows in the core
4//! tables. They are intentionally serialization-friendly via `serde` so
5//! that they can be emitted as JSON in reports without an intermediate
6//! DTO layer.
7
8use chrono::{DateTime, Utc};
9use serde::{Deserialize, Serialize};
10
11/// A single commit observed in a repository.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Commit {
14 /// Primary key (database-assigned).
15 pub id: i64,
16
17 /// Full git OID (hex).
18 pub sha: String,
19
20 /// Foreign key into [`Author`].
21 pub author_id: Option<i64>,
22
23 /// Author display name as recorded in the commit.
24 pub author_name: String,
25
26 /// Author email as recorded in the commit.
27 pub author_email: String,
28
29 /// Author timestamp (UTC).
30 pub timestamp: DateTime<Utc>,
31
32 /// Commit message body (raw).
33 pub message: String,
34
35 /// Repository identifier (path or canonical name).
36 pub repository: String,
37
38 /// Number of files changed.
39 pub files_changed: u32,
40
41 /// Lines added.
42 pub insertions: u32,
43
44 /// Lines deleted.
45 pub deletions: u32,
46
47 /// Foreign key into [`Classification`], if classified.
48 pub classification_id: Option<i64>,
49
50 /// Confidence assigned by the classifier (0.0–1.0).
51 pub confidence: Option<f64>,
52
53 /// True for merge commits (parents > 1).
54 pub is_merge: bool,
55}
56
57/// A canonical author / developer identity.
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct Author {
60 /// Primary key (database-assigned).
61 pub id: i64,
62
63 /// Canonical display name.
64 pub canonical_name: String,
65
66 /// Canonical email address.
67 pub canonical_email: String,
68
69 /// JSON-encoded array of alias strings (names or emails).
70 pub aliases: String,
71}
72
73/// A classification verdict produced by the cascade.
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Classification {
76 /// Primary key (database-assigned).
77 pub id: i64,
78
79 /// Top-level category (e.g. `feature`, `bugfix`, `chore`).
80 pub category: String,
81
82 /// Optional finer-grained label.
83 pub subcategory: Option<String>,
84
85 /// Associated ticket identifier (e.g. `API-123`), if any.
86 pub ticket_id: Option<String>,
87
88 /// Confidence in this verdict (0.0–1.0).
89 pub confidence: f64,
90
91 /// Which tier of the cascade produced this verdict.
92 pub method: ClassificationMethod,
93}
94
95/// File-level change record attached to a commit.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct FileChange {
98 /// Primary key (database-assigned).
99 pub id: i64,
100
101 /// Foreign key into [`Commit`].
102 pub commit_id: i64,
103
104 /// Relative path within the repository.
105 pub path: String,
106
107 /// Type of change.
108 pub change_type: ChangeType,
109
110 /// Lines added in this file.
111 pub insertions: u32,
112
113 /// Lines deleted in this file.
114 pub deletions: u32,
115}
116
117/// A pull request record (typically GitHub).
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct PullRequest {
120 /// Primary key (database-assigned).
121 pub id: i64,
122
123 /// PR number within its repository.
124 pub pr_number: u64,
125
126 /// PR title.
127 pub title: String,
128
129 /// Author login.
130 pub author: String,
131
132 /// Lifecycle state.
133 pub state: PrState,
134
135 /// PR creation timestamp (UTC).
136 pub created_at: DateTime<Utc>,
137
138 /// Merge timestamp, if merged.
139 pub merged_at: Option<DateTime<Utc>>,
140
141 /// JSON-encoded array of commit SHAs in the PR.
142 pub commit_shas: String,
143}
144
145/// Cascade tier that produced a classification.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147#[serde(rename_all = "snake_case")]
148pub enum ClassificationMethod {
149 /// Matched a deterministic exact rule.
150 ExactRule,
151 /// Matched a regex rule.
152 RegexRule,
153 /// Matched via fuzzy similarity.
154 FuzzyMatch,
155 /// Assigned by an LLM fallback.
156 LlmFallback,
157 /// Set manually by a user override.
158 Manual,
159}
160
161impl ClassificationMethod {
162 /// Stable string representation used for DB storage.
163 pub fn as_str(&self) -> &'static str {
164 match self {
165 ClassificationMethod::ExactRule => "exact_rule",
166 ClassificationMethod::RegexRule => "regex_rule",
167 ClassificationMethod::FuzzyMatch => "fuzzy_match",
168 ClassificationMethod::LlmFallback => "llm_fallback",
169 ClassificationMethod::Manual => "manual",
170 }
171 }
172}
173
174/// File change kind for [`FileChange`].
175#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
176#[serde(rename_all = "snake_case")]
177pub enum ChangeType {
178 /// File was added.
179 Added,
180 /// File contents were modified.
181 Modified,
182 /// File was deleted.
183 Deleted,
184 /// File was renamed (and possibly modified).
185 Renamed,
186}
187
188impl ChangeType {
189 /// Stable string representation used for DB storage.
190 pub fn as_str(&self) -> &'static str {
191 match self {
192 ChangeType::Added => "added",
193 ChangeType::Modified => "modified",
194 ChangeType::Deleted => "deleted",
195 ChangeType::Renamed => "renamed",
196 }
197 }
198}
199
200/// Lifecycle state of a [`PullRequest`].
201#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
202#[serde(rename_all = "snake_case")]
203pub enum PrState {
204 /// PR is open.
205 Open,
206 /// PR was closed without merging.
207 Closed,
208 /// PR was merged.
209 Merged,
210}
211
212impl PrState {
213 /// Stable string representation used for DB storage.
214 pub fn as_str(&self) -> &'static str {
215 match self {
216 PrState::Open => "open",
217 PrState::Closed => "closed",
218 PrState::Merged => "merged",
219 }
220 }
221}