1use crate::domain::{
4 CodeFeatureFlagGraph, CodeFeatureFlagRequest, CodeFileFingerprint, CodeImpactRequest,
5 CodeIndexBatch, CodeIndexCheckpoint, CodeIndexSession, CodeIndexSnapshot, CodeIndexSummary,
6 CodeIndexTaskQueueStatus, CodeIndexTaskRecord, CodeRepositoryCrossEdge,
7 CodeRepositoryRegistration, CodeRepositoryRemovalSummary, CodeRepositoryReport,
8 CodeRepositorySet, CodeRepositorySetMember, CodeRepositorySetRefreshSummary,
9 CodeRepositorySetRefreshTaskRecord, CodeRepositorySetStatus, CodeRepositoryStatus,
10 CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest, CodeScopeRetentionSummary,
11 CodeSymbolGenerationCounts, CodebaseViewRequest, CodebaseViewSnapshot,
12 SoftwareGlobalProjection, SoftwareGlobalRequest,
13};
14
15use super::{StorageError, StorageFuture};
16
17pub const CODE_INDEX_TASK_LEASE_RECOVERY_UNAVAILABLE: &str =
19 "code index task lease recovery is unavailable";
20
21pub const CODE_INDEX_TASK_LEASE_RENEWAL_UNAVAILABLE: &str =
23 "code index task lease renewal is unavailable";
24
25#[derive(Debug, Clone, Default, PartialEq, Eq)]
27pub struct CodeImpactChanges {
28 pub paths: Vec<String>,
29 pub deleted_symbol_names: Vec<String>,
30}
31
32#[derive(Debug, Clone, Default, PartialEq, Eq)]
34pub struct CodeRepositorySetEdgeSelector {
35 pub origin_files: Vec<(String, String)>,
36 pub target_records: Vec<(String, String, String)>,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct CodeIndexTaskSeed {
42 pub repository_id: String,
43 pub alias: String,
44 pub ref_selector: String,
45 pub resolved_commit_sha: String,
46 pub tree_hash: String,
47 pub source_scope: String,
48 pub path_filters: Vec<String>,
49 pub language_filters: Vec<String>,
50 pub mode: crate::domain::CodeIndexMode,
51 pub input_fingerprint: String,
52 pub resource_budget: crate::domain::CodeIndexResourceBudget,
53 pub payload_json: String,
54 pub now_ms: u64,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct CodeIndexTaskClaimRequest {
60 pub task_id: Option<String>,
61 pub lease_owner: String,
62 pub lease_duration_ms: u64,
63 pub max_attempts: u32,
64 pub now_ms: u64,
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct CodeIndexTaskLeaseRenewal {
70 pub task_id: String,
71 pub lease_owner: String,
72 pub attempt_count: u32,
73 pub lease_duration_ms: u64,
74 pub now_ms: u64,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct CodeIndexTaskLeaseRecord {
80 pub task_id: String,
81 pub lease_owner: String,
82 pub lease_expires_at_ms: Option<u64>,
83 pub attempt_count: u32,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct CodeIndexTaskLeaseRecovery {
89 pub task_ids: Vec<String>,
90 pub now_ms: u64,
91 pub max_attempts: u32,
92 pub error_kind: String,
93 pub error_message: String,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct CodeIndexTaskCompletion {
99 pub task_id: String,
100 pub lease_owner: String,
101 pub attempt_count: u32,
102 pub now_ms: u64,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct CodeIndexTaskFailure {
108 pub task_id: String,
109 pub lease_owner: String,
110 pub attempt_count: u32,
111 pub error_kind: String,
112 pub error_message: String,
113 pub retry_backoff_ms: u64,
114 pub max_attempts: u32,
115 pub now_ms: u64,
116}
117
118#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct CodeScopeRetentionRequest {
121 pub repository_id: String,
122 pub active_scope: String,
123 pub retain_recent_successful_scopes: usize,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct CodeRepositorySetSeed {
129 pub alias: String,
130 pub description: Option<String>,
131 pub default_ref_policy_json: String,
132 pub now_ms: u64,
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137pub struct CodeRepositorySetMemberSeed {
138 pub set_alias: String,
139 pub repository_id: String,
140 pub repository_alias: String,
141 pub ref_selector: String,
142 pub resolved_commit_sha: String,
143 pub source_scope: String,
144 pub path_filters: Vec<String>,
145 pub language_filters: Vec<String>,
146 pub priority: i32,
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
151pub struct CodeRepositorySetRefreshTaskSeed {
152 pub set_id: String,
153 pub set_alias: String,
154 pub input_fingerprint: String,
155 pub now_ms: u64,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct CodeRepositorySetRefreshTaskClaimRequest {
161 pub task_id: Option<String>,
162 pub lease_owner: String,
163 pub lease_duration_ms: u64,
164 pub max_attempts: u32,
165 pub now_ms: u64,
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct CodeRepositorySetRefreshTaskCompletion {
171 pub task_id: String,
172 pub lease_owner: String,
173 pub attempt_count: u32,
174 pub now_ms: u64,
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct CodeRepositorySetRefreshTaskFailure {
180 pub task_id: String,
181 pub lease_owner: String,
182 pub attempt_count: u32,
183 pub error_kind: String,
184 pub error_message: String,
185 pub retry_backoff_ms: u64,
186 pub max_attempts: u32,
187 pub now_ms: u64,
188}
189
190pub trait CodeRepositoryStore: Send + Sync {
192 fn upsert_code_repository(
193 &self,
194 registration: CodeRepositoryRegistration,
195 ) -> StorageFuture<'_, CodeRepositoryStatus>;
196
197 fn code_repository_status(
198 &self,
199 repository: String,
200 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>>;
201
202 fn list_code_repositories(&self) -> StorageFuture<'_, Vec<CodeRepositoryStatus>> {
203 Box::pin(async { Ok(Vec::new()) })
204 }
205
206 fn remove_code_repository(
207 &self,
208 repository: String,
209 now_ms: u64,
210 ) -> StorageFuture<'_, Option<CodeRepositoryRemovalSummary>> {
211 Box::pin(async move {
212 Err(StorageError::InvalidInput(format!(
213 "code repository removal for '{repository}' at {now_ms} is unavailable"
214 )))
215 })
216 }
217
218 fn code_repository_scope_status(
219 &self,
220 repository: String,
221 resolved_commit_sha: String,
222 path_filters: Vec<String>,
223 language_filters: Vec<String>,
224 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>>;
225
226 fn latest_code_repository_scope_status(
227 &self,
228 _repository: String,
229 _path_filters: Vec<String>,
230 _language_filters: Vec<String>,
231 ) -> StorageFuture<'_, Option<CodeRepositoryStatus>> {
232 Box::pin(async { Ok(None) })
233 }
234
235 fn queue_code_index_task(
236 &self,
237 task: CodeIndexTaskSeed,
238 ) -> StorageFuture<'_, CodeIndexTaskRecord>;
239
240 fn claim_code_index_task(
241 &self,
242 request: CodeIndexTaskClaimRequest,
243 ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
244
245 fn recover_code_index_task_leases(
246 &self,
247 _now_ms: u64,
248 _max_attempts: u32,
249 ) -> StorageFuture<'_, ()> {
250 Box::pin(async {
251 Err(StorageError::InvalidInput(
252 CODE_INDEX_TASK_LEASE_RECOVERY_UNAVAILABLE.to_owned(),
253 ))
254 })
255 }
256
257 fn running_code_index_task_leases(&self) -> StorageFuture<'_, Vec<CodeIndexTaskLeaseRecord>> {
258 Box::pin(async { Ok(Vec::new()) })
259 }
260
261 fn recover_code_index_task_leases_by_task(
262 &self,
263 _request: CodeIndexTaskLeaseRecovery,
264 ) -> StorageFuture<'_, usize> {
265 Box::pin(async {
266 Err(StorageError::InvalidInput(
267 CODE_INDEX_TASK_LEASE_RECOVERY_UNAVAILABLE.to_owned(),
268 ))
269 })
270 }
271
272 fn reset_code_index_tasks(
273 &self,
274 _repository_id: String,
275 _now_ms: u64,
276 ) -> StorageFuture<'_, Vec<CodeIndexTaskRecord>> {
277 Box::pin(async {
278 Err(StorageError::InvalidInput(
279 "code index task reset is unavailable".to_owned(),
280 ))
281 })
282 }
283
284 fn renew_code_index_task_lease(
285 &self,
286 _request: CodeIndexTaskLeaseRenewal,
287 ) -> StorageFuture<'_, CodeIndexTaskRecord> {
288 Box::pin(async {
289 Err(StorageError::InvalidInput(
290 CODE_INDEX_TASK_LEASE_RENEWAL_UNAVAILABLE.to_owned(),
291 ))
292 })
293 }
294
295 fn complete_code_index_task(
296 &self,
297 request: CodeIndexTaskCompletion,
298 ) -> StorageFuture<'_, CodeIndexTaskRecord>;
299
300 fn fail_code_index_task(
301 &self,
302 request: CodeIndexTaskFailure,
303 ) -> StorageFuture<'_, CodeIndexTaskRecord>;
304
305 fn code_index_task(&self, task_id: String) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
306
307 fn active_code_index_task(
308 &self,
309 repository_id: String,
310 ) -> StorageFuture<'_, Option<CodeIndexTaskRecord>>;
311
312 fn code_index_task_queue_status(&self) -> StorageFuture<'_, CodeIndexTaskQueueStatus> {
313 Box::pin(async { Ok(CodeIndexTaskQueueStatus::default()) })
314 }
315
316 fn code_index_checkpoint(
317 &self,
318 source_scope: String,
319 ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>>;
320
321 fn latest_code_index_checkpoint(
322 &self,
323 _repository_id: String,
324 ) -> StorageFuture<'_, Option<CodeIndexCheckpoint>> {
325 Box::pin(async { Ok(None) })
326 }
327
328 fn code_scope_retention(
329 &self,
330 repository_id: String,
331 ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
332
333 fn prune_code_repository_scopes(
334 &self,
335 request: CodeScopeRetentionRequest,
336 ) -> StorageFuture<'_, CodeScopeRetentionSummary>;
337
338 fn code_file_fingerprints(
339 &self,
340 repository_id: String,
341 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>>;
342
343 fn code_file_fingerprints_for_scope(
344 &self,
345 source_scope: String,
346 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
347 Box::pin(async move {
348 Err(StorageError::InvalidInput(format!(
349 "code file fingerprints for scope '{source_scope}' are unavailable"
350 )))
351 })
352 }
353
354 fn code_file_fingerprints_for_paths(
355 &self,
356 source_scope: String,
357 paths: Vec<String>,
358 ) -> StorageFuture<'_, Vec<CodeFileFingerprint>> {
359 Box::pin(async move {
360 let mut fingerprints = self.code_file_fingerprints_for_scope(source_scope).await?;
361 fingerprints.retain(|fingerprint| paths.iter().any(|path| path == &fingerprint.path));
362 Ok(fingerprints)
363 })
364 }
365
366 fn code_file_candidate_paths_for_scope(
367 &self,
368 source_scope: String,
369 _path_filters: Vec<String>,
370 _language_filters: Vec<String>,
371 _exclude_generated: bool,
372 _limit: usize,
373 ) -> StorageFuture<'_, Vec<String>> {
374 Box::pin(async move {
375 Err(StorageError::InvalidInput(format!(
376 "bounded code file candidate paths for scope '{source_scope}' are unavailable"
377 )))
378 })
379 }
380
381 fn code_file_candidate_paths_for_query_scope(
382 &self,
383 source_scope: String,
384 _query: String,
385 path_filters: Vec<String>,
386 language_filters: Vec<String>,
387 exclude_generated: bool,
388 limit: usize,
389 ) -> StorageFuture<'_, Vec<String>> {
390 self.code_file_candidate_paths_for_scope(
391 source_scope,
392 path_filters,
393 language_filters,
394 exclude_generated,
395 limit,
396 )
397 }
398
399 fn apply_code_index_snapshot(
400 &self,
401 snapshot: CodeIndexSnapshot,
402 ) -> StorageFuture<'_, CodeIndexSummary>;
403
404 fn clear_code_workspace_state(
405 &self,
406 _repository_id: String,
407 _source_scope: String,
408 ) -> StorageFuture<'_, ()> {
409 Box::pin(async { Ok(()) })
410 }
411
412 fn begin_code_index_session(
413 &self,
414 session: CodeIndexSession,
415 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
416 Box::pin(async move {
417 Err(StorageError::InvalidInput(format!(
418 "checkpointed code index sessions for scope '{}' are unavailable",
419 session.source_scope
420 )))
421 })
422 }
423
424 fn apply_code_index_batch(
425 &self,
426 batch: CodeIndexBatch,
427 ) -> StorageFuture<'_, CodeIndexCheckpoint> {
428 Box::pin(async move {
429 Err(StorageError::InvalidInput(format!(
430 "checkpointed code index batches for scope '{}' are unavailable",
431 batch.source_scope
432 )))
433 })
434 }
435
436 fn finalize_code_index_session(
437 &self,
438 session: CodeIndexSession,
439 ) -> StorageFuture<'_, CodeIndexSummary> {
440 Box::pin(async move {
441 Err(StorageError::InvalidInput(format!(
442 "checkpointed code index finalization for scope '{}' is unavailable",
443 session.source_scope
444 )))
445 })
446 }
447
448 fn search_code(
449 &self,
450 request: CodeRetrievalRequest,
451 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
452
453 fn search_code_feature_flags(
454 &self,
455 request: CodeFeatureFlagRequest,
456 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
457 Box::pin(async move {
458 Err(StorageError::InvalidInput(format!(
459 "code feature flag search for repository '{}' is unavailable",
460 request.repository.repository
461 )))
462 })
463 }
464
465 fn search_code_feature_flags_scope(
466 &self,
467 source_scope: String,
468 _request: CodeFeatureFlagRequest,
469 ) -> StorageFuture<'_, Vec<CodeFeatureFlagGraph>> {
470 Box::pin(async move {
471 Err(StorageError::InvalidInput(format!(
472 "code feature flag search for source scope '{source_scope}' is unavailable"
473 )))
474 })
475 }
476
477 fn search_code_scope(
478 &self,
479 source_scope: String,
480 _request: CodeRetrievalRequest,
481 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
482 Box::pin(async move {
483 Err(StorageError::InvalidInput(format!(
484 "code search for source scope '{source_scope}' is unavailable"
485 )))
486 })
487 }
488
489 fn analyze_code_impact(
490 &self,
491 request: CodeImpactRequest,
492 changes: CodeImpactChanges,
493 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>>;
494
495 fn analyze_code_impact_scope(
496 &self,
497 source_scope: String,
498 _request: CodeImpactRequest,
499 _changes: CodeImpactChanges,
500 ) -> StorageFuture<'_, Vec<CodeRetrievalHit>> {
501 Box::pin(async move {
502 Err(StorageError::InvalidInput(format!(
503 "code impact analysis for source scope '{source_scope}' is unavailable"
504 )))
505 })
506 }
507
508 fn codebase_view_snapshot(
509 &self,
510 source_scope: String,
511 _request: CodebaseViewRequest,
512 _row_limit: usize,
513 ) -> StorageFuture<'_, CodebaseViewSnapshot> {
514 Box::pin(async move {
515 Err(StorageError::InvalidInput(format!(
516 "codebase view snapshot for source scope '{source_scope}' is unavailable"
517 )))
518 })
519 }
520
521 fn code_repository_totals(&self) -> StorageFuture<'_, CodeRepositoryTotals> {
522 Box::pin(async { Ok(CodeRepositoryTotals::default()) })
523 }
524
525 fn code_repository_report(
526 &self,
527 repository: String,
528 ) -> StorageFuture<'_, CodeRepositoryReport> {
529 Box::pin(async move {
530 Err(StorageError::InvalidInput(format!(
531 "code repository report for '{repository}' is unavailable"
532 )))
533 })
534 }
535
536 fn code_repository_scope_symbol_generation_counts(
537 &self,
538 source_scope: String,
539 ) -> StorageFuture<'_, CodeSymbolGenerationCounts> {
540 Box::pin(async move {
541 Err(StorageError::InvalidInput(format!(
542 "code symbol generation counts for source scope '{source_scope}' are unavailable"
543 )))
544 })
545 }
546
547 fn refresh_software_global_projection(
548 &self,
549 source_scope: String,
550 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
551 Box::pin(async move {
552 Err(StorageError::InvalidInput(format!(
553 "software global projection for source scope '{source_scope}' is unavailable"
554 )))
555 })
556 }
557
558 fn software_global_projection(
559 &self,
560 request: SoftwareGlobalRequest,
561 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
562 Box::pin(async move {
563 Err(StorageError::InvalidInput(format!(
564 "software global projection for repository '{}' is unavailable",
565 request.repository.repository
566 )))
567 })
568 }
569
570 fn software_global_projection_for_scope(
571 &self,
572 source_scope: String,
573 _request: SoftwareGlobalRequest,
574 ) -> StorageFuture<'_, SoftwareGlobalProjection> {
575 Box::pin(async move {
576 Err(StorageError::InvalidInput(format!(
577 "software global projection for source scope '{source_scope}' is unavailable"
578 )))
579 })
580 }
581
582 fn create_code_repository_set(
583 &self,
584 _seed: CodeRepositorySetSeed,
585 ) -> StorageFuture<'_, CodeRepositorySet> {
586 Box::pin(async {
587 Err(StorageError::InvalidInput(
588 "repository set storage is unavailable".to_owned(),
589 ))
590 })
591 }
592
593 fn add_code_repository_set_member(
594 &self,
595 _seed: CodeRepositorySetMemberSeed,
596 ) -> StorageFuture<'_, CodeRepositorySetMember> {
597 Box::pin(async {
598 Err(StorageError::InvalidInput(
599 "repository set member storage is unavailable".to_owned(),
600 ))
601 })
602 }
603
604 fn remove_code_repository_set_member(
605 &self,
606 _set_alias: String,
607 _repository_alias: String,
608 ) -> StorageFuture<'_, CodeRepositorySetMember> {
609 Box::pin(async {
610 Err(StorageError::InvalidInput(
611 "repository set member storage is unavailable".to_owned(),
612 ))
613 })
614 }
615
616 fn code_repository_set(
617 &self,
618 _set_alias: String,
619 ) -> StorageFuture<'_, Option<CodeRepositorySet>> {
620 Box::pin(async { Ok(None) })
621 }
622
623 fn code_repository_set_status(
624 &self,
625 _set_alias: String,
626 ) -> StorageFuture<'_, Option<CodeRepositorySetStatus>> {
627 Box::pin(async { Ok(None) })
628 }
629
630 fn refresh_code_repository_set_overlay(
631 &self,
632 _set_alias: String,
633 _now_ms: u64,
634 ) -> StorageFuture<'_, CodeRepositorySetRefreshSummary> {
635 Box::pin(async {
636 Err(StorageError::InvalidInput(
637 "repository set overlay refresh is unavailable".to_owned(),
638 ))
639 })
640 }
641
642 fn code_repository_set_cross_edges(
643 &self,
644 _set_id: String,
645 ) -> StorageFuture<'_, Vec<CodeRepositoryCrossEdge>> {
646 Box::pin(async { Ok(Vec::new()) })
647 }
648
649 fn code_repository_set_cross_edges_for_selector(
650 &self,
651 set_id: String,
652 _selector: CodeRepositorySetEdgeSelector,
653 ) -> StorageFuture<'_, Vec<CodeRepositoryCrossEdge>> {
654 self.code_repository_set_cross_edges(set_id)
655 }
656
657 fn queue_code_repository_set_refresh_task(
658 &self,
659 _task: CodeRepositorySetRefreshTaskSeed,
660 ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
661 Box::pin(async {
662 Err(StorageError::InvalidInput(
663 "repository set refresh task storage is unavailable".to_owned(),
664 ))
665 })
666 }
667
668 fn claim_code_repository_set_refresh_task(
669 &self,
670 _request: CodeRepositorySetRefreshTaskClaimRequest,
671 ) -> StorageFuture<'_, Option<CodeRepositorySetRefreshTaskRecord>> {
672 Box::pin(async { Ok(None) })
673 }
674
675 fn complete_code_repository_set_refresh_task(
676 &self,
677 _request: CodeRepositorySetRefreshTaskCompletion,
678 ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
679 Box::pin(async {
680 Err(StorageError::InvalidInput(
681 "repository set refresh task storage is unavailable".to_owned(),
682 ))
683 })
684 }
685
686 fn fail_code_repository_set_refresh_task(
687 &self,
688 _request: CodeRepositorySetRefreshTaskFailure,
689 ) -> StorageFuture<'_, CodeRepositorySetRefreshTaskRecord> {
690 Box::pin(async {
691 Err(StorageError::InvalidInput(
692 "repository set refresh task storage is unavailable".to_owned(),
693 ))
694 })
695 }
696}
697
698#[cfg(test)]
699#[path = "code_tests.rs"]
700mod tests;