relay_knowledge/application/code_repository/impact/
mod.rs1use std::{collections::BTreeMap, path::PathBuf};
4
5use crate::{
6 api::{ApiError, ApiMetadata, CodeRepositoryImpactResponse, RequestContext},
7 code::{
8 changed_paths_for_diff_with_filters, changed_paths_for_filesystem_diff,
9 deleted_symbol_names_for_diff, partition_changed_paths_for_selector,
10 },
11 domain::CodeImpactRequest,
12 storage::CodeImpactChanges,
13};
14
15use crate::application::service::RelayKnowledgeService;
16
17use super::{
18 blocking::run_blocking_code,
19 errors::storage_api_error,
20 repository::{registration_from_status, required_code_repository},
21 scope::{
22 indexed_source_scope, missing_indexed_source_scope_error, resolve_code_ref_for_selector,
23 resolved_code_scope_status,
24 },
25};
26
27impl RelayKnowledgeService {
28 pub async fn impact_code_repository(
30 &self,
31 mut request: CodeImpactRequest,
32 context: RequestContext,
33 ) -> Result<CodeRepositoryImpactResponse, ApiError> {
34 let store = self.store().await.map_err(storage_api_error)?;
35 let status = required_code_repository(&store, &request.repository.repository).await?;
36 let head_commit =
37 resolve_code_ref_for_selector(&status, &request.repository, request.head_ref.clone())
38 .await?;
39 request.repository.ref_selector = head_commit.clone();
40 let scoped_status =
41 resolved_code_scope_status(&store, &status, &request.repository).await?;
42 let root = PathBuf::from(status.root_path.clone());
43 let base_ref = request.base_ref.clone();
44 let head_ref = head_commit.clone();
45 let path_filters = scoped_status.path_filters.clone();
46 let language_filters = scoped_status.language_filters.clone();
47 let base_fingerprints = if base_ref.starts_with("filesystem:") {
48 let mut base_selector = request.repository.clone();
49 base_selector.ref_selector = base_ref.clone();
50 match resolved_code_scope_status(&store, &status, &base_selector).await {
51 Ok(base_status) => match base_status.last_indexed_scope_id {
52 Some(source_scope) => Some(
53 store
54 .code_file_fingerprints_for_scope(source_scope)
55 .await
56 .map_err(storage_api_error)?,
57 ),
58 None => None,
59 },
60 Err(_) => None,
61 }
62 } else {
63 None
64 };
65 let changed_paths = if let Some(base_fingerprints) = base_fingerprints {
66 run_blocking_code(move || {
67 let previous_hashes = base_fingerprints
68 .into_iter()
69 .map(|file| (file.path, file.blob_hash))
70 .collect::<BTreeMap<_, _>>();
71 changed_paths_for_filesystem_diff(
72 &root,
73 &head_ref,
74 &path_filters,
75 &language_filters,
76 &previous_hashes,
77 )
78 })
79 .await?
80 } else {
81 run_blocking_code(move || {
82 changed_paths_for_diff_with_filters(
83 root,
84 &base_ref,
85 &head_ref,
86 &path_filters,
87 &language_filters,
88 )
89 })
90 .await?
91 };
92 let registration = registration_from_status(&status);
93 let path_groups = {
94 let registration = registration.clone();
95 let selector = request.repository.clone();
96 let changed_paths = changed_paths.clone();
97 run_blocking_code(move || {
98 partition_changed_paths_for_selector(®istration, &selector, changed_paths)
99 })
100 .await?
101 };
102 let selector = request.repository.clone();
103 let base_ref = request.base_ref.clone();
104 let head_ref = head_commit;
105 let deleted_symbol_names = run_blocking_code(move || {
106 deleted_symbol_names_for_diff(®istration, &selector, &base_ref, &head_ref)
107 })
108 .await?;
109 let source_scope = indexed_source_scope(&scoped_status)
110 .ok_or_else(|| missing_indexed_source_scope_error(&scoped_status))?;
111 let results = store
112 .analyze_code_impact_scope(
113 source_scope,
114 request.clone(),
115 CodeImpactChanges {
116 paths: changed_paths.clone(),
117 deleted_symbol_names,
118 },
119 )
120 .await
121 .map_err(storage_api_error)?;
122 let graph_version = store
123 .current_graph_version()
124 .await
125 .map_err(storage_api_error)?;
126 let scope = crate::api::CodeRepositoryScopeMetadata::from_status(
127 &scoped_status,
128 &request.repository,
129 request.head_ref.clone(),
130 );
131
132 Ok(CodeRepositoryImpactResponse {
133 metadata: ApiMetadata::graph_only(&context, graph_version),
134 scope,
135 request,
136 path_groups,
137 results,
138 })
139 }
140}