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 #[serde(default)]
90 pub declared_business_domains: Vec<CodebaseViewDeclaredBusinessDomain>,
91 pub files: Vec<CodebaseViewFile>,
92 pub symbols: Vec<CodebaseViewSymbol>,
93 pub imports: Vec<CodeImportRecord>,
94 pub calls: Vec<CodebaseViewCall>,
95 pub routes: Vec<CodeRouteRecord>,
96 pub dependencies: Vec<CodebaseViewDependency>,
97 pub feature_flags: Vec<CodeFeatureFlagRecord>,
98 pub truncated: bool,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103pub struct CodebaseViewDeclaredBusinessDomain {
104 pub id: String,
105 pub name: String,
106 pub source_path: String,
107 pub evidence_id: String,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
112pub struct CodebaseViewFile {
113 pub path: String,
114 pub language_id: String,
115 pub parse_status: String,
116 pub line_count: usize,
117 pub is_generated: bool,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct CodebaseViewSymbol {
123 pub symbol_snapshot_id: String,
124 pub path: String,
125 pub language_id: String,
126 pub name: String,
127 pub qualified_name: String,
128 pub kind: String,
129 pub line_range: RepositoryCodeRange,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct CodebaseViewCall {
135 pub call: CodeCallRecord,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub callee_path: Option<String>,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
142pub struct CodebaseViewDependency {
143 pub dependency_id: String,
144 pub path: String,
145 pub language_id: String,
146 pub ecosystem: String,
147 pub package_name: String,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub requirement: Option<String>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub resolved_version: Option<String>,
152 pub dependency_group: String,
153 pub source_kind: String,
154 pub line_range: RepositoryCodeRange,
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub struct CodebaseViewNode {
160 pub id: String,
161 pub label: String,
162 pub node_kind: String,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub path: Option<String>,
165 pub confidence: f64,
166 pub evidence_ids: Vec<String>,
167}
168
169#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct CodebaseViewEdge {
172 pub id: String,
173 pub source_id: String,
174 pub target_id: String,
175 pub edge_kind: String,
176 pub confidence: f64,
177 pub evidence_ids: Vec<String>,
178}
179
180#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct CodebaseViewSection {
183 pub id: String,
184 pub title: String,
185 pub narrative: String,
186 pub confidence: f64,
187 pub node_ids: Vec<String>,
188 pub edge_ids: Vec<String>,
189 pub evidence_ids: Vec<String>,
190 #[serde(default, skip_serializing_if = "Vec::is_empty")]
191 pub diagnostics: Vec<String>,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub struct CodebaseViewEvidence {
197 pub id: String,
198 pub evidence_kind: String,
199 pub path: String,
200 #[serde(skip_serializing_if = "Option::is_none")]
201 pub symbol: Option<String>,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub line_range: Option<RepositoryCodeRange>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub edge_kind: Option<String>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub retrieval_layer: Option<CodeRetrievalLayer>,
208 pub detail: String,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
213pub struct CodebaseViewBudget {
214 pub requested_limit: usize,
215 pub snapshot_row_limit: usize,
216 pub snapshot_truncated: bool,
217 pub nodes_truncated: bool,
218 pub edges_truncated: bool,
219 pub sections_truncated: bool,
220 pub evidence_truncated: bool,
221}
222
223impl CodebaseViewBudget {
224 pub const fn new(
226 requested_limit: usize,
227 snapshot_row_limit: usize,
228 snapshot_truncated: bool,
229 ) -> Self {
230 Self {
231 requested_limit,
232 snapshot_row_limit,
233 snapshot_truncated,
234 nodes_truncated: false,
235 edges_truncated: false,
236 sections_truncated: false,
237 evidence_truncated: false,
238 }
239 }
240}