1use serde::{Deserialize, Serialize};
4
5use super::{
6 dependencies::CodeDependencyRecord,
7 error::DomainError,
8 repository::{
9 CodeCallRecord, CodeFeatureFlagRecord, CodeFileDiagnostic, CodeImportRecord, CodeIndexMode,
10 CodePathTombstone, CodeRouteRecord, RepositoryCodeChunkRecord, RepositoryCodeFileRecord,
11 RepositoryCodeReferenceRecord, RepositoryCodeSymbolRecord,
12 },
13 workspace::CodeMonorepoWorkspace,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
18pub struct CodeIndexSnapshot {
19 pub repository_id: String,
20 pub source_scope: String,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub base_resolved_commit_sha: Option<String>,
23 pub resolved_commit_sha: String,
24 pub tree_hash: String,
25 pub path_filters: Vec<String>,
26 pub language_filters: Vec<String>,
27 pub full_replace: bool,
28 pub changed_path_count: usize,
29 pub skipped_unchanged_count: usize,
30 pub deleted_paths: Vec<String>,
31 pub tombstones: Vec<CodePathTombstone>,
32 pub files: Vec<RepositoryCodeFileRecord>,
33 pub symbols: Vec<RepositoryCodeSymbolRecord>,
34 pub references: Vec<RepositoryCodeReferenceRecord>,
35 pub imports: Vec<CodeImportRecord>,
36 pub calls: Vec<CodeCallRecord>,
37 pub dependencies: Vec<CodeDependencyRecord>,
38 pub feature_flags: Vec<CodeFeatureFlagRecord>,
39 pub routes: Vec<CodeRouteRecord>,
40 pub chunks: Vec<RepositoryCodeChunkRecord>,
41 #[serde(default)]
42 pub workspaces: Vec<CodeMonorepoWorkspace>,
43 pub diagnostics: Vec<CodeFileDiagnostic>,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
48pub struct CodeIndexResourceBudget {
49 pub max_files_per_batch: usize,
50 pub max_bytes_per_batch: usize,
51 pub max_rows_per_batch: usize,
52}
53
54impl CodeIndexResourceBudget {
55 pub const DEFAULT_MAX_FILES_PER_BATCH: usize = 512;
56 pub const DEFAULT_MAX_BYTES_PER_BATCH: usize = 16 * 1024 * 1024;
57 pub const DEFAULT_MAX_ROWS_PER_BATCH: usize = 150_000;
58
59 pub fn new(
61 max_files_per_batch: usize,
62 max_bytes_per_batch: usize,
63 max_rows_per_batch: usize,
64 ) -> Result<Self, DomainError> {
65 if max_files_per_batch == 0 {
66 return Err(DomainError::invalid(
67 "max_files_per_batch",
68 "must be greater than zero",
69 ));
70 }
71 if max_bytes_per_batch == 0 {
72 return Err(DomainError::invalid(
73 "max_bytes_per_batch",
74 "must be greater than zero",
75 ));
76 }
77 if max_rows_per_batch == 0 {
78 return Err(DomainError::invalid(
79 "max_rows_per_batch",
80 "must be greater than zero",
81 ));
82 }
83
84 Ok(Self {
85 max_files_per_batch,
86 max_bytes_per_batch,
87 max_rows_per_batch,
88 })
89 }
90}
91
92impl Default for CodeIndexResourceBudget {
93 fn default() -> Self {
94 Self {
95 max_files_per_batch: Self::DEFAULT_MAX_FILES_PER_BATCH,
96 max_bytes_per_batch: Self::DEFAULT_MAX_BYTES_PER_BATCH,
97 max_rows_per_batch: Self::DEFAULT_MAX_ROWS_PER_BATCH,
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct CodeIndexSession {
105 pub repository_id: String,
106 pub source_scope: String,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub base_resolved_commit_sha: Option<String>,
109 pub resolved_commit_sha: String,
110 pub tree_hash: String,
111 pub path_filters: Vec<String>,
112 pub language_filters: Vec<String>,
113 pub full_replace: bool,
114 pub total_path_count: usize,
115 pub changed_path_count: usize,
116 pub skipped_unchanged_count: usize,
117 pub deleted_paths: Vec<String>,
118 pub tombstones: Vec<CodePathTombstone>,
119 #[serde(default)]
120 pub workspaces: Vec<CodeMonorepoWorkspace>,
121 pub resource_budget: CodeIndexResourceBudget,
122}
123
124#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct CodeIndexBatch {
127 pub repository_id: String,
128 pub source_scope: String,
129 pub batch_index: usize,
130 pub parsed_byte_count: usize,
131 pub files: Vec<RepositoryCodeFileRecord>,
132 pub symbols: Vec<RepositoryCodeSymbolRecord>,
133 pub references: Vec<RepositoryCodeReferenceRecord>,
134 pub imports: Vec<CodeImportRecord>,
135 pub dependencies: Vec<CodeDependencyRecord>,
136 pub feature_flags: Vec<CodeFeatureFlagRecord>,
137 pub routes: Vec<CodeRouteRecord>,
138 pub chunks: Vec<RepositoryCodeChunkRecord>,
139 pub diagnostics: Vec<CodeFileDiagnostic>,
140}
141
142impl CodeIndexBatch {
143 pub fn row_count(&self) -> usize {
144 self.files
145 .len()
146 .saturating_add(self.symbols.len())
147 .saturating_add(self.references.len())
148 .saturating_add(self.imports.len())
149 .saturating_add(self.dependencies.len())
150 .saturating_add(self.feature_flags.len())
151 .saturating_add(self.routes.len())
152 .saturating_add(self.chunks.len())
153 .saturating_add(self.diagnostics.len())
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
159pub struct CodeIndexCheckpoint {
160 pub repository_id: String,
161 pub source_scope: String,
162 pub state: String,
163 pub total_path_count: usize,
164 pub parsed_file_count: usize,
165 pub committed_file_count: usize,
166 pub committed_symbol_count: usize,
167 pub committed_reference_count: usize,
168 pub committed_chunk_count: usize,
169 pub batch_count: usize,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub last_path: Option<String>,
172 pub resource_budget: CodeIndexResourceBudget,
173 pub updated_at_ms: u64,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
178#[serde(rename_all = "snake_case")]
179pub enum CodeIndexTaskState {
180 Queued,
181 Running,
182 Succeeded,
183 Retrying,
184 Failed,
185 DeadLetter,
186 Cancelled,
187}
188
189impl CodeIndexTaskState {
190 pub const fn as_str(self) -> &'static str {
192 match self {
193 Self::Queued => "queued",
194 Self::Running => "running",
195 Self::Succeeded => "succeeded",
196 Self::Retrying => "retrying",
197 Self::Failed => "failed",
198 Self::DeadLetter => "dead_letter",
199 Self::Cancelled => "cancelled",
200 }
201 }
202
203 pub fn parse(value: &str) -> Result<Self, DomainError> {
205 match value {
206 "queued" => Ok(Self::Queued),
207 "running" => Ok(Self::Running),
208 "succeeded" => Ok(Self::Succeeded),
209 "retrying" => Ok(Self::Retrying),
210 "failed" => Ok(Self::Failed),
211 "dead_letter" => Ok(Self::DeadLetter),
212 "cancelled" => Ok(Self::Cancelled),
213 _ => Err(DomainError::invalid(
214 "code_index_task_state",
215 "unknown code index task state",
216 )),
217 }
218 }
219
220 pub const fn is_unfinished(self) -> bool {
222 matches!(self, Self::Queued | Self::Running | Self::Retrying)
223 }
224}
225
226#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
228pub struct CodeIndexTaskRecord {
229 pub task_id: String,
230 pub repository_id: String,
231 pub alias: String,
232 pub ref_selector: String,
233 pub resolved_commit_sha: String,
234 pub tree_hash: String,
235 pub source_scope: String,
236 pub path_filters: Vec<String>,
237 pub language_filters: Vec<String>,
238 pub mode: CodeIndexMode,
239 pub state: CodeIndexTaskState,
240 #[serde(skip_serializing_if = "Option::is_none")]
241 pub lease_owner: Option<String>,
242 #[serde(skip_serializing_if = "Option::is_none")]
243 pub lease_expires_at_ms: Option<u64>,
244 pub attempt_count: u32,
245 pub next_retry_at_ms: u64,
246 pub input_fingerprint: String,
247 pub resource_budget: CodeIndexResourceBudget,
248 pub payload_json: String,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub last_error_kind: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub last_error_message: Option<String>,
253 pub created_at_ms: u64,
254 pub updated_at_ms: u64,
255}
256
257#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
259pub struct CodeIndexTaskQueueStatus {
260 pub queued_task_count: usize,
261 pub running_task_count: usize,
262 pub retrying_task_count: usize,
263 pub dead_letter_task_count: usize,
264 pub running_lease_count: usize,
265 #[serde(skip_serializing_if = "Option::is_none")]
266 pub last_error: Option<String>,
267}
268
269#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
271pub struct CodeScopeRetentionSummary {
272 pub repository_id: String,
273 pub retained_scope_count: usize,
274 pub prunable_scope_count: usize,
275 pub pruned_scope_count: usize,
276 pub retained_scopes: Vec<String>,
277 pub prunable_scopes: Vec<String>,
278 pub pruned_scopes: Vec<String>,
279}
280
281#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
283pub struct CodeIndexProgressSummary {
284 pub git_file_count: usize,
285 pub blob_read_count: usize,
286 pub parsed_file_count: usize,
287 pub sqlite_write_count: usize,
288 pub skipped_file_count: usize,
289 pub degraded_file_count: usize,
290 pub batch_count: usize,
291 pub checkpoint_file_count: usize,
292 pub resource_budget: CodeIndexResourceBudget,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297pub struct CodeIndexSummary {
298 pub repository_id: String,
299 pub source_scope: String,
300 pub resolved_commit_sha: String,
301 pub tree_hash: String,
302 pub indexed_file_count: usize,
303 pub changed_path_count: usize,
304 pub skipped_unchanged_count: usize,
305 pub deleted_path_count: usize,
306 pub symbol_count: usize,
307 #[serde(default)]
308 pub handwritten_symbol_count: usize,
309 #[serde(default)]
310 pub generated_symbol_count: usize,
311 pub reference_count: usize,
312 pub chunk_count: usize,
313 pub degraded_file_count: usize,
314 pub progress: CodeIndexProgressSummary,
315}
316
317#[cfg(test)]
318mod mod_tests;