1use serde::{Deserialize, Serialize};
4
5use super::{
6 CodeCallRecord, CodeFeatureFlagRecord, CodeImportRecord, CodeRepositorySelector,
7 CodeRetrievalLayer, CodeRouteRecord, DomainError, FreshnessPolicy, RepositoryCodeRange,
8 error::required_text,
9};
10
11const MAX_CODEBASE_VIEW_CHANGED_PATHS: usize = 200;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "snake_case")]
16pub enum CodebaseViewKind {
17 ArchitectureLayers,
18 BusinessDomains,
19 DependencyTour,
20 ProcessFlow,
21 AffectedScope,
22}
23
24impl CodebaseViewKind {
25 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::ArchitectureLayers => "architecture_layers",
29 Self::BusinessDomains => "business_domains",
30 Self::DependencyTour => "dependency_tour",
31 Self::ProcessFlow => "process_flow",
32 Self::AffectedScope => "affected_scope",
33 }
34 }
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct CodebaseViewRequest {
40 pub repository: CodeRepositorySelector,
41 pub view_kind: CodebaseViewKind,
42 pub freshness_policy: FreshnessPolicy,
43 pub limit: usize,
44 #[serde(default, skip_serializing_if = "Vec::is_empty")]
45 pub changed_paths: Vec<String>,
46}
47
48impl CodebaseViewRequest {
49 pub fn new(
51 repository: CodeRepositorySelector,
52 view_kind: CodebaseViewKind,
53 freshness_policy: FreshnessPolicy,
54 limit: usize,
55 changed_paths: Vec<String>,
56 ) -> Result<Self, DomainError> {
57 let limit = match limit {
58 1..=100 => limit,
59 0 => return Err(DomainError::invalid("limit", "must be greater than zero")),
60 _ => return Err(DomainError::invalid("limit", "must be 100 or less")),
61 };
62 let changed_paths = changed_paths
63 .into_iter()
64 .map(|path| required_text("changed_path", path))
65 .collect::<Result<Vec<_>, _>>()?;
66 if changed_paths.len() > MAX_CODEBASE_VIEW_CHANGED_PATHS {
67 return Err(DomainError::invalid(
68 "changed_paths",
69 "must contain 200 or fewer entries",
70 ));
71 }
72
73 Ok(Self {
74 repository,
75 view_kind,
76 freshness_policy,
77 limit,
78 changed_paths,
79 })
80 }
81}
82
83#[cfg(test)]
84mod mod_tests;
85
86#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
88pub struct CodebaseViewSnapshot {
89 pub files: Vec<CodebaseViewFile>,
90 pub symbols: Vec<CodebaseViewSymbol>,
91 pub imports: Vec<CodeImportRecord>,
92 pub calls: Vec<CodebaseViewCall>,
93 pub routes: Vec<CodeRouteRecord>,
94 pub dependencies: Vec<CodebaseViewDependency>,
95 pub feature_flags: Vec<CodeFeatureFlagRecord>,
96 pub truncated: bool,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
101pub struct CodebaseViewFile {
102 pub path: String,
103 pub language_id: String,
104 pub parse_status: String,
105 pub line_count: usize,
106 pub is_generated: bool,
107}
108
109#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111pub struct CodebaseViewSymbol {
112 pub symbol_snapshot_id: String,
113 pub path: String,
114 pub language_id: String,
115 pub name: String,
116 pub qualified_name: String,
117 pub kind: String,
118 pub line_range: RepositoryCodeRange,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct CodebaseViewCall {
124 pub call: CodeCallRecord,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 pub callee_path: Option<String>,
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
131pub struct CodebaseViewDependency {
132 pub dependency_id: String,
133 pub path: String,
134 pub language_id: String,
135 pub ecosystem: String,
136 pub package_name: String,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub requirement: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub resolved_version: Option<String>,
141 pub dependency_group: String,
142 pub source_kind: String,
143 pub line_range: RepositoryCodeRange,
144}
145
146#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
148pub struct CodebaseViewNode {
149 pub id: String,
150 pub label: String,
151 pub node_kind: String,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub path: Option<String>,
154 pub confidence: f64,
155 pub evidence_ids: Vec<String>,
156}
157
158#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub struct CodebaseViewEdge {
161 pub id: String,
162 pub source_id: String,
163 pub target_id: String,
164 pub edge_kind: String,
165 pub confidence: f64,
166 pub evidence_ids: Vec<String>,
167}
168
169#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct CodebaseViewSection {
172 pub id: String,
173 pub title: String,
174 pub narrative: String,
175 pub confidence: f64,
176 pub node_ids: Vec<String>,
177 pub edge_ids: Vec<String>,
178 pub evidence_ids: Vec<String>,
179 #[serde(default, skip_serializing_if = "Vec::is_empty")]
180 pub diagnostics: Vec<String>,
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
185pub struct CodebaseViewEvidence {
186 pub id: String,
187 pub evidence_kind: String,
188 pub path: String,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub symbol: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub line_range: Option<RepositoryCodeRange>,
193 #[serde(skip_serializing_if = "Option::is_none")]
194 pub edge_kind: Option<String>,
195 #[serde(skip_serializing_if = "Option::is_none")]
196 pub retrieval_layer: Option<CodeRetrievalLayer>,
197 pub detail: String,
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
202pub struct CodebaseViewBudget {
203 pub requested_limit: usize,
204 pub snapshot_row_limit: usize,
205 pub snapshot_truncated: bool,
206 pub nodes_truncated: bool,
207 pub edges_truncated: bool,
208 pub sections_truncated: bool,
209 pub evidence_truncated: bool,
210}
211
212impl CodebaseViewBudget {
213 pub const fn new(
215 requested_limit: usize,
216 snapshot_row_limit: usize,
217 snapshot_truncated: bool,
218 ) -> Self {
219 Self {
220 requested_limit,
221 snapshot_row_limit,
222 snapshot_truncated,
223 nodes_truncated: false,
224 edges_truncated: false,
225 sections_truncated: false,
226 evidence_truncated: false,
227 }
228 }
229}