1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6 domain::{
7 CodeImpactPathGroups, CodeImpactRequest, CodeIndexSummary, CodeRepositoryRegistration,
8 CodeRepositoryReport, CodeRepositoryScopePreview, CodeRepositorySelector,
9 CodeRepositoryStatus, CodeRepositoryTotals, CodeRetrievalHit, CodeRetrievalRequest,
10 CommitReceipt, ConfidenceScore, EvidenceExtractionMetadata, EvidenceModality, EvidenceSpan,
11 ExtractionDiagnostic, FactStatus, FreshnessPolicy, FusionDiagnostics, GraphVersionRange,
12 IndexKind, IndexStatus, LayoutRegion, ProposalConflictRecord, ProposalRecord,
13 ProposalState, RerankDiagnostics, RetrievalBackendStatus, RetrievalBudgetUsed,
14 RetrievalHit, RetrievalMode, RetrievedContextPack, ServiceDefinitionPlan,
15 ServiceManagerAction, ServiceOperatorStatus, WorkerKind, WorkerStatus, WorkerTaskRecord,
16 },
17 storage::{GraphInspection, IndexCursor, IndexRefreshDiagnostics},
18};
19
20use super::{AgentProtocolStatus, ApiMetadata, RuntimeStatus};
21
22pub const GRAPH_CANVAS_DEFAULT_LIMIT: usize = 250;
23pub const GRAPH_CANVAS_MAX_LIMIT: usize = 1000;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum GraphCanvasKind {
29 Knowledge,
30 Code,
31 Mixed,
32}
33
34impl GraphCanvasKind {
35 pub fn parse(value: &str) -> Result<Self, String> {
37 match value {
38 "knowledge" => Ok(Self::Knowledge),
39 "code" => Ok(Self::Code),
40 "mixed" => Ok(Self::Mixed),
41 _ => Err(format!("unsupported graph canvas kind '{value}'")),
42 }
43 }
44
45 pub const fn as_str(self) -> &'static str {
47 match self {
48 Self::Knowledge => "knowledge",
49 Self::Code => "code",
50 Self::Mixed => "mixed",
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57pub struct GraphCanvasRequest {
58 pub kind: GraphCanvasKind,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub source_scope: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub query: Option<String>,
63 pub limit: usize,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
68pub struct GraphCanvasNode {
69 pub id: String,
70 pub kind: String,
71 pub label: String,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub subtitle: Option<String>,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub source_scope: Option<String>,
76 pub graph_version: u64,
77 pub weight: u32,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 pub status: Option<String>,
80 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
81 pub details: BTreeMap<String, String>,
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub struct GraphCanvasEdge {
87 pub id: String,
88 pub kind: String,
89 pub source: String,
90 pub target: String,
91 pub label: String,
92 pub graph_version: u64,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub confidence_basis_points: Option<u16>,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub evidence_count: Option<usize>,
97 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
98 pub details: BTreeMap<String, String>,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct GraphCanvasSummary {
104 pub kind: GraphCanvasKind,
105 pub node_count: usize,
106 pub edge_count: usize,
107 pub truncated: bool,
108 pub available_kinds: Vec<String>,
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub struct GraphCanvasResponse {
114 pub metadata: ApiMetadata,
115 pub nodes: Vec<GraphCanvasNode>,
116 pub edges: Vec<GraphCanvasEdge>,
117 pub summary: GraphCanvasSummary,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct IngestEvidence {
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub id: Option<String>,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub source_path: Option<String>,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub span: Option<EvidenceSpan>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub confidence: Option<ConfidenceScore>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub status: Option<FactStatus>,
133 pub content: String,
134 #[serde(default)]
135 pub entity_labels: Vec<String>,
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub extraction: Option<IngestEvidenceExtraction>,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
142pub struct IngestEvidenceExtraction {
143 pub modality: EvidenceModality,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub source_uri: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub source_hash: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub media_hash: Option<String>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub extractor: Option<String>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub extractor_version: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub observed_at: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub parent_evidence_id: Option<String>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub layout_region: Option<LayoutRegion>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub embedding_model: Option<String>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub embedding_dimension: Option<u16>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub diagnostic: Option<ExtractionDiagnostic>,
166}
167
168impl IngestEvidenceExtraction {
169 pub fn into_domain_metadata(self) -> EvidenceExtractionMetadata {
171 EvidenceExtractionMetadata {
172 modality: self.modality,
173 source_uri: self.source_uri,
174 source_hash: self.source_hash,
175 media_hash: self.media_hash,
176 extractor: self.extractor,
177 extractor_version: self.extractor_version,
178 observed_at: self.observed_at,
179 parent_evidence_id: self.parent_evidence_id,
180 layout_region: self.layout_region,
181 embedding_model: self.embedding_model,
182 embedding_dimension: self.embedding_dimension,
183 diagnostic: self
184 .diagnostic
185 .unwrap_or_else(|| EvidenceExtractionMetadata::text_span().diagnostic),
186 }
187 }
188}
189
190#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192pub struct IngestRelation {
193 pub id: String,
194 pub source_entity_label: String,
195 pub relation_type: String,
196 pub target_entity_label: String,
197 #[serde(default)]
198 pub evidence_ids: Vec<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub confidence: Option<ConfidenceScore>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub status: Option<FactStatus>,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub version_range: Option<GraphVersionRange>,
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209pub struct IngestClaim {
210 pub id: String,
211 pub subject_entity_label: String,
212 pub predicate: String,
213 pub object: String,
214 #[serde(default)]
215 pub evidence_ids: Vec<String>,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub confidence: Option<ConfidenceScore>,
218 #[serde(skip_serializing_if = "Option::is_none")]
219 pub status: Option<FactStatus>,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub version_range: Option<GraphVersionRange>,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226pub struct IngestEvent {
227 pub id: String,
228 pub event_type: String,
229 #[serde(default)]
230 pub entity_labels: Vec<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub occurred_at: Option<String>,
233 #[serde(default)]
234 pub evidence_ids: Vec<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub confidence: Option<ConfidenceScore>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub status: Option<FactStatus>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub version_range: Option<GraphVersionRange>,
241}
242
243#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
245pub struct IngestRequest {
246 pub source_scope: String,
247 #[serde(default)]
248 pub evidence: Vec<IngestEvidence>,
249 #[serde(default)]
250 pub relations: Vec<IngestRelation>,
251 #[serde(default)]
252 pub claims: Vec<IngestClaim>,
253 #[serde(default)]
254 pub events: Vec<IngestEvent>,
255}
256
257#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
259pub struct IngestResponse {
260 pub metadata: ApiMetadata,
261 pub receipt: CommitReceipt,
262 pub indexes: Vec<IndexStatus>,
263 #[serde(skip_serializing_if = "Option::is_none")]
264 pub index_refresh_error: Option<String>,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
269pub struct MultimodalExtractionRequest {
270 pub source_scope: String,
271 pub parent_evidence_id: String,
272 pub derived_evidence: Vec<IngestEvidence>,
273}
274
275#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
277pub struct MultimodalExtractionResponse {
278 pub metadata: ApiMetadata,
279 pub parent_evidence_id: String,
280 pub derived_evidence_count: usize,
281 pub receipt: CommitReceipt,
282 pub indexes: Vec<IndexStatus>,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub index_refresh_error: Option<String>,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
289pub struct HybridRetrievalRequest {
290 pub query: String,
291 #[serde(skip_serializing_if = "Option::is_none")]
292 pub source_scope: Option<String>,
293 pub limit: usize,
294 pub freshness: FreshnessPolicy,
295}
296
297impl HybridRetrievalRequest {
298 pub fn new(query: impl Into<String>) -> Self {
300 Self {
301 query: query.into(),
302 source_scope: None,
303 limit: 10,
304 freshness: FreshnessPolicy::default(),
305 }
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
311pub struct HybridRetrievalResponse {
312 pub metadata: ApiMetadata,
313 pub context_pack: RetrievedContextPack,
314 pub retrieval_mode: RetrievalMode,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub source_scope: Option<String>,
317 pub freshness: FreshnessPolicy,
318 pub results: Vec<RetrievalHit>,
319 pub fusion: FusionDiagnostics,
320 pub rerank: RerankDiagnostics,
321 #[serde(default, skip_serializing_if = "Vec::is_empty")]
322 pub backend_statuses: Vec<RetrievalBackendStatus>,
323 pub truncated: bool,
324 pub budget_used: RetrievalBudgetUsed,
325 #[serde(skip_serializing_if = "Option::is_none")]
326 pub degraded_reason: Option<String>,
327 pub indexes: Vec<IndexStatus>,
328}
329
330#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
332pub struct GraphInspectionRequest {
333 #[serde(skip_serializing_if = "Option::is_none")]
334 pub source_scope: Option<String>,
335}
336
337#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
339pub struct GraphInspectionResponse {
340 pub metadata: ApiMetadata,
341 pub graph: GraphInspection,
342 pub repository_code_totals: CodeRepositoryTotals,
343}
344
345#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
347pub struct IndexRefreshRequest {
348 #[serde(default)]
349 pub kinds: Vec<IndexKind>,
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
354pub struct IndexRefreshResponse {
355 pub metadata: ApiMetadata,
356 pub indexes: Vec<IndexStatus>,
357 pub index_cursors: Vec<IndexCursor>,
358 pub diagnostics: IndexRefreshDiagnostics,
359}
360
361#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
363pub struct FileIndexRequest {
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub source_scope: Option<String>,
366 #[serde(default)]
367 pub roots: Vec<String>,
368}
369
370#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
372pub struct FileIndexResponse {
373 pub metadata: ApiMetadata,
374 pub summary: crate::storage::FileIndexScanSummary,
375}
376
377#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
379pub struct FileQueryRequest {
380 pub query: String,
381 #[serde(skip_serializing_if = "Option::is_none")]
382 pub source_scope: Option<String>,
383 #[serde(skip_serializing_if = "Option::is_none")]
384 pub root_id: Option<String>,
385 pub limit: usize,
386}
387
388#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
390pub struct FileQueryResponse {
391 pub metadata: ApiMetadata,
392 pub query: String,
393 #[serde(skip_serializing_if = "Option::is_none")]
394 pub source_scope: Option<String>,
395 #[serde(skip_serializing_if = "Option::is_none")]
396 pub root_id: Option<String>,
397 pub results: Vec<crate::storage::FileSearchHit>,
398 pub truncated: bool,
399 pub duration_ms: u64,
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub degraded_reason: Option<String>,
402}
403
404#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
406pub struct ServiceStatusResponse {
407 pub metadata: ApiMetadata,
408 pub service_name: String,
409 pub mode: String,
410 pub background_enabled: bool,
411 pub silent_updates_enabled: bool,
412 pub service_definition_path: String,
413 pub index_refresh: IndexRefreshDiagnostics,
414 pub file_index: crate::storage::FileIndexDiagnostics,
415 pub agent_protocols: AgentProtocolStatus,
416 pub operator: ServiceOperatorStatus,
417 pub workers: Vec<WorkerStatus>,
418 pub proposal_backlog: usize,
419 pub audit_sink: AuditSinkStatus,
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
424pub struct AuditSinkStatus {
425 pub durable: bool,
426 pub event_count: usize,
427 #[serde(skip_serializing_if = "Option::is_none")]
428 pub last_error: Option<String>,
429}
430
431#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
433pub struct WorkerStatusRequest {
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub kind: Option<WorkerKind>,
436}
437
438#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
440pub struct WorkerStatusResponse {
441 pub metadata: ApiMetadata,
442 pub workers: Vec<WorkerStatus>,
443}
444
445#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
447pub struct WorkerRunRequest {
448 #[serde(skip_serializing_if = "Option::is_none")]
449 pub kind: Option<WorkerKind>,
450}
451
452#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
454pub struct WorkerRunResponse {
455 pub metadata: ApiMetadata,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub task: Option<WorkerTaskRecord>,
458 #[serde(default)]
459 pub proposals: Vec<ProposalRecord>,
460 pub workers: Vec<WorkerStatus>,
461 #[serde(skip_serializing_if = "Option::is_none")]
462 pub degraded_reason: Option<String>,
463}
464
465#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
467pub struct ProposalListApiRequest {
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub state: Option<ProposalState>,
470 pub limit: usize,
471}
472
473#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
475pub struct ProposalListResponse {
476 pub metadata: ApiMetadata,
477 pub proposals: Vec<ProposalRecord>,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
482pub struct ProposalShowResponse {
483 pub metadata: ApiMetadata,
484 pub proposal: ProposalRecord,
485 pub conflicts: Vec<ProposalConflictRecord>,
486 pub payload: serde_json::Value,
487}
488
489#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
491pub struct ProposalDecisionApiRequest {
492 pub actor: String,
493 #[serde(skip_serializing_if = "Option::is_none")]
494 pub reason: Option<String>,
495}
496
497#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
499pub struct ProposalDecisionResponse {
500 pub metadata: ApiMetadata,
501 pub proposal: ProposalRecord,
502 #[serde(skip_serializing_if = "Option::is_none")]
503 pub receipt: Option<CommitReceipt>,
504 #[serde(skip_serializing_if = "Option::is_none")]
505 pub index_refresh_error: Option<String>,
506}
507
508#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
510pub struct AuditQueryApiRequest {
511 #[serde(skip_serializing_if = "Option::is_none")]
512 pub operation: Option<String>,
513 pub limit: usize,
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
518pub struct AuditQueryResponse {
519 pub metadata: ApiMetadata,
520 pub events: Vec<crate::domain::AuditEventRecord>,
521}
522
523#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
525pub struct ServicePlanRequest {
526 pub action: ServiceManagerAction,
527}
528
529#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
531pub struct ServicePlanResponse {
532 pub metadata: ApiMetadata,
533 pub plan: ServiceDefinitionPlan,
534}
535
536#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
538pub struct ServiceDefinitionWriteResponse {
539 pub metadata: ApiMetadata,
540 pub plan: ServiceDefinitionPlan,
541 pub written: bool,
542}
543
544#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
546pub struct ServiceOperatorResponse {
547 pub metadata: ApiMetadata,
548 pub operator: ServiceOperatorStatus,
549}
550
551#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
553pub struct ServiceRecoveryReport {
554 pub metadata: ApiMetadata,
555 pub graph_version: u64,
556 pub stale_index_kinds: Vec<IndexKind>,
557 pub refreshed_index_kinds: Vec<IndexKind>,
558 pub index_lag_max: u64,
559 pub task_queue_depth: usize,
560 pub dead_letter_count: usize,
561 pub heartbeat_state: String,
562}
563
564#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
566pub struct HealthResponse {
567 pub metadata: ApiMetadata,
568 pub healthy: bool,
569 pub graph: GraphInspection,
570 pub repository_code_totals: CodeRepositoryTotals,
571 pub indexes: Vec<IndexStatus>,
572 pub index_cursors: Vec<IndexCursor>,
573 pub index_refresh: IndexRefreshDiagnostics,
574 pub file_index: crate::storage::FileIndexDiagnostics,
575 pub runtime: RuntimeStatus,
576}
577
578#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
580pub struct EmbeddingProviderProbeResponse {
581 pub metadata: ApiMetadata,
582 pub ok: bool,
583 pub provider: Option<String>,
584 pub model: String,
585 pub dimension: u32,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 pub latency_ms: Option<u64>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 pub error_code: Option<String>,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 pub error_message: Option<String>,
592 #[serde(skip_serializing_if = "Option::is_none")]
593 pub retryable: Option<bool>,
594}
595
596#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
598pub struct CodeRepositoryRegisterRequest {
599 pub root_path: String,
600 pub alias: String,
601 #[serde(default)]
602 pub path_filters: Vec<String>,
603 #[serde(default)]
604 pub language_filters: Vec<String>,
605}
606
607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
609pub struct CodeRepositoryRegisterResponse {
610 pub metadata: ApiMetadata,
611 pub registration: CodeRepositoryRegistration,
612 pub status: CodeRepositoryStatus,
613}
614
615#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
617pub struct CodeRepositoryIndexResponse {
618 pub metadata: ApiMetadata,
619 pub scope: CodeRepositoryScopeMetadata,
620 pub summary: CodeIndexSummary,
621 pub status: CodeRepositoryStatus,
622}
623
624#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
626pub struct CodeRepositoryScopePreviewResponse {
627 pub metadata: ApiMetadata,
628 pub scope: CodeRepositoryScopeMetadata,
629 pub preview: CodeRepositoryScopePreview,
630}
631
632#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
634pub struct CodeRepositoryScopeMetadata {
635 pub scope_id: String,
636 pub repository_id: String,
637 pub alias: String,
638 pub requested_ref: String,
639 pub resolved_commit_sha: String,
640 pub tree_hash: String,
641 pub path_filters: Vec<String>,
642 pub language_filters: Vec<String>,
643 pub index_versions: Vec<String>,
644 pub stale: bool,
645}
646
647impl CodeRepositoryScopeMetadata {
648 pub fn from_status(
650 status: &CodeRepositoryStatus,
651 selector: &CodeRepositorySelector,
652 requested_ref: impl Into<String>,
653 ) -> Self {
654 Self {
655 scope_id: status.last_indexed_scope_id.clone().unwrap_or_default(),
656 repository_id: status.repository_id.clone(),
657 alias: status.alias.clone(),
658 requested_ref: requested_ref.into(),
659 resolved_commit_sha: status.last_indexed_commit.clone().unwrap_or_default(),
660 tree_hash: status.tree_hash.clone().unwrap_or_default(),
661 path_filters: merged_filters(&status.path_filters, &selector.path_filters),
662 language_filters: merged_filters(&status.language_filters, &selector.language_filters),
663 index_versions: vec![format!(
664 "code:{}:{}",
665 status
666 .last_indexed_scope_id
667 .as_deref()
668 .unwrap_or("unscoped"),
669 status.tree_hash.as_deref().unwrap_or("unindexed")
670 )],
671 stale: status.stale,
672 }
673 }
674}
675
676#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
678pub struct CodeRepositoryQueryResponse {
679 pub metadata: ApiMetadata,
680 pub scope: CodeRepositoryScopeMetadata,
681 pub request: CodeRetrievalRequest,
682 pub results: Vec<CodeRetrievalHit>,
683 #[serde(skip_serializing_if = "Option::is_none")]
684 pub degraded_reason: Option<String>,
685}
686
687#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
689pub struct CodeRepositoryImpactResponse {
690 pub metadata: ApiMetadata,
691 pub scope: CodeRepositoryScopeMetadata,
692 pub request: CodeImpactRequest,
693 pub path_groups: CodeImpactPathGroups,
694 pub results: Vec<CodeRetrievalHit>,
695}
696
697#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
699pub struct CodeRepositoryStatusResponse {
700 pub metadata: ApiMetadata,
701 pub status: CodeRepositoryStatus,
702}
703
704#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
706pub struct CodeRepositoryReportResponse {
707 pub metadata: ApiMetadata,
708 pub scope: CodeRepositoryScopeMetadata,
709 pub report: CodeRepositoryReport,
710}
711
712fn merged_filters(base: &[String], request: &[String]) -> Vec<String> {
713 let mut merged = Vec::new();
714 for value in base.iter().chain(request.iter()) {
715 if !merged.contains(value) {
716 merged.push(value.clone());
717 }
718 }
719
720 merged
721}