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