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 =
36 required_code_repository(store.as_ref(), &request.repository.repository).await?;
37 let head_commit =
38 resolve_code_ref_for_selector(&status, &request.repository, request.head_ref.clone())
39 .await?;
40 request.repository.ref_selector = head_commit.clone();
41 let scoped_status =
42 resolved_code_scope_status(&store, &status, &request.repository).await?;
43 let root = PathBuf::from(status.root_path.clone());
44 let base_ref = request.base_ref.clone();
45 let head_ref = head_commit.clone();
46 let path_filters = scoped_status.path_filters.clone();
47 let language_filters = scoped_status.language_filters.clone();
48 let base_fingerprints = if base_ref.starts_with("filesystem:") {
49 let mut base_selector = request.repository.clone();
50 base_selector.ref_selector = base_ref.clone();
51 match resolved_code_scope_status(&store, &status, &base_selector).await {
52 Ok(base_status) => match base_status.last_indexed_scope_id {
53 Some(source_scope) => Some(
54 store
55 .code_file_fingerprints_for_scope(source_scope)
56 .await
57 .map_err(storage_api_error)?,
58 ),
59 None => None,
60 },
61 Err(_) => None,
62 }
63 } else {
64 None
65 };
66 let changed_paths = if let Some(base_fingerprints) = base_fingerprints {
67 run_blocking_code(move || {
68 let previous_hashes = base_fingerprints
69 .into_iter()
70 .map(|file| (file.path, file.blob_hash))
71 .collect::<BTreeMap<_, _>>();
72 changed_paths_for_filesystem_diff(
73 &root,
74 &head_ref,
75 &path_filters,
76 &language_filters,
77 &previous_hashes,
78 )
79 })
80 .await?
81 } else {
82 run_blocking_code(move || {
83 changed_paths_for_diff_with_filters(
84 root,
85 &base_ref,
86 &head_ref,
87 &path_filters,
88 &language_filters,
89 )
90 })
91 .await?
92 };
93 let registration = registration_from_status(&status);
94 let path_groups = {
95 let registration = registration.clone();
96 let selector = request.repository.clone();
97 let changed_paths = changed_paths.clone();
98 run_blocking_code(move || {
99 partition_changed_paths_for_selector(®istration, &selector, changed_paths)
100 })
101 .await?
102 };
103 let selector = request.repository.clone();
104 let base_ref = request.base_ref.clone();
105 let head_ref = head_commit;
106 let deleted_symbol_names = run_blocking_code(move || {
107 deleted_symbol_names_for_diff(®istration, &selector, &base_ref, &head_ref)
108 })
109 .await?;
110 let source_scope = indexed_source_scope(&scoped_status)
111 .ok_or_else(|| missing_indexed_source_scope_error(&scoped_status))?;
112 let results = store
113 .analyze_code_impact_scope(
114 source_scope,
115 request.clone(),
116 CodeImpactChanges {
117 paths: changed_paths.clone(),
118 deleted_symbol_names,
119 },
120 )
121 .await
122 .map_err(storage_api_error)?;
123 let graph_version = store
124 .current_graph_version()
125 .await
126 .map_err(storage_api_error)?;
127 let scope = crate::api::CodeRepositoryScopeMetadata::from_status(
128 &scoped_status,
129 &request.repository,
130 request.head_ref.clone(),
131 );
132
133 Ok(CodeRepositoryImpactResponse {
134 metadata: ApiMetadata::graph_only(&context, graph_version),
135 scope,
136 request,
137 path_groups,
138 results,
139 })
140 }
141}