relay_knowledge/application/code_repository/repository/
mod.rs1#[cfg(test)]
2#[path = "mod_tests.rs"]
3mod mod_tests;
4mod staleness;
5mod status;
6#[cfg(test)]
7#[path = "test_support.rs"]
8pub(super) mod test_support;
9#[cfg(test)]
10#[path = "workspace_scope_tests.rs"]
11mod workspace_scope_tests;
12mod worktree;
13#[cfg(test)]
14#[path = "worktree_review_tests.rs"]
15mod worktree_review_tests;
16
17use crate::{
18 api::{
19 ApiError, ApiMetadata, CodeRepositoryListResponse, CodeRepositoryRegisterRequest,
20 CodeRepositoryRegisterResponse, CodeRepositoryRemoveResponse, CodeRepositoryReportResponse,
21 CodeRepositoryStatusResponse, RequestContext,
22 },
23 code::{REGISTRATION_LANGUAGE_FILTER_ERROR, register_repository},
24 domain::CodeRepositorySelector,
25};
26use std::path::PathBuf;
27
28use crate::application::service::RelayKnowledgeService;
29
30use super::{
31 blocking::run_blocking_code, clock::now_millis, errors::storage_api_error,
32 indexing::recover_code_index_task_leases,
33};
34
35pub(super) use staleness::annotate_query_result_staleness;
36pub(super) use status::{
37 code_status_checkpoint, registration_from_status, required_code_repository,
38};
39pub(super) use worktree::ensure_worktree_overlay_matches_current_worktree;
40
41impl RelayKnowledgeService {
42 pub(crate) async fn registered_code_repository_root(
44 &self,
45 repository: &str,
46 ) -> Result<PathBuf, ApiError> {
47 let store = self.store().await.map_err(storage_api_error)?;
48 let status = required_code_repository(store.as_ref(), repository).await?;
49
50 Ok(PathBuf::from(status.root_path))
51 }
52
53 pub async fn list_indexed_code_repositories(
55 &self,
56 context: RequestContext,
57 ) -> Result<CodeRepositoryListResponse, ApiError> {
58 let store = self.store().await.map_err(storage_api_error)?;
59 let repositories = store
60 .list_code_repositories()
61 .await
62 .map_err(storage_api_error)?
63 .into_iter()
64 .filter(|status| status.last_indexed_scope_id.is_some())
65 .collect();
66 let graph_version = store
67 .current_graph_version()
68 .await
69 .map_err(storage_api_error)?;
70
71 Ok(CodeRepositoryListResponse {
72 metadata: ApiMetadata::graph_only(&context, graph_version),
73 repositories,
74 })
75 }
76
77 pub async fn register_code_repository(
79 &self,
80 request: CodeRepositoryRegisterRequest,
81 context: RequestContext,
82 ) -> Result<CodeRepositoryRegisterResponse, ApiError> {
83 if !request.language_filters.is_empty() {
84 return Err(ApiError::invalid_argument(
85 REGISTRATION_LANGUAGE_FILTER_ERROR,
86 ));
87 }
88 let registration = run_blocking_code(move || {
89 register_repository(
90 request.root_path,
91 request.alias,
92 request.path_filters,
93 request.language_filters,
94 )
95 })
96 .await?;
97 let store = self.store().await.map_err(storage_api_error)?;
98 let status = store
99 .upsert_code_repository(registration.clone())
100 .await
101 .map_err(storage_api_error)?;
102 let _ = self.refresh_watched_code_repository(&status).await;
103 let graph_version = store
104 .current_graph_version()
105 .await
106 .map_err(storage_api_error)?;
107
108 Ok(CodeRepositoryRegisterResponse {
109 metadata: ApiMetadata::graph_only(&context, graph_version),
110 registration,
111 status,
112 })
113 }
114
115 pub async fn remove_code_repository(
117 &self,
118 repository: String,
119 context: RequestContext,
120 ) -> Result<CodeRepositoryRemoveResponse, ApiError> {
121 let store = self.store().await.map_err(storage_api_error)?;
122 let now_ms = now_millis();
123 recover_code_index_task_leases(&store, now_ms).await?;
124 let removed_status = required_code_repository(store.as_ref(), &repository).await?;
125 let summary = store
126 .remove_code_repository(removed_status.repository_id.clone(), now_ms)
127 .await
128 .map_err(storage_api_error)?
129 .ok_or_else(|| {
130 ApiError::storage_unavailable("removed code repository disappeared before delete")
131 })?;
132 let _ = self
133 .remove_watched_code_repository(&removed_status.alias, &removed_status.repository_id)
134 .await;
135 let graph_version = store
136 .current_graph_version()
137 .await
138 .map_err(storage_api_error)?;
139
140 Ok(CodeRepositoryRemoveResponse {
141 metadata: ApiMetadata::graph_only(&context, graph_version),
142 removed_status,
143 summary,
144 })
145 }
146
147 pub async fn code_repository_status(
148 &self,
149 selector: CodeRepositorySelector,
150 context: RequestContext,
151 ) -> Result<CodeRepositoryStatusResponse, ApiError> {
152 let store = self.store().await.map_err(storage_api_error)?;
153 let status = required_code_repository(store.as_ref(), &selector.repository).await?;
154 recover_code_index_task_leases(&store, now_millis()).await?;
155 let active_task = store
156 .active_code_index_task(status.repository_id.clone())
157 .await
158 .map_err(storage_api_error)?;
159 let checkpoint =
160 code_status_checkpoint(store.as_ref(), &status, active_task.as_ref()).await?;
161 let retention = store
162 .code_scope_retention(status.repository_id.clone())
163 .await
164 .map_err(storage_api_error)?;
165 let graph_version = store
166 .current_graph_version()
167 .await
168 .map_err(storage_api_error)?;
169
170 Ok(CodeRepositoryStatusResponse {
171 metadata: ApiMetadata::graph_only(&context, graph_version),
172 status,
173 active_task,
174 checkpoint,
175 retention,
176 })
177 }
178
179 pub(crate) async fn code_repository_is_registered(
180 &self,
181 repository: String,
182 ) -> Result<bool, ApiError> {
183 let selector = CodeRepositorySelector::new(repository, "HEAD", Vec::new(), Vec::new())
184 .map_err(|error| ApiError::invalid_argument(error.to_string()))?;
185 let store = self.store().await.map_err(storage_api_error)?;
186 store
187 .code_repository_status(selector.repository)
188 .await
189 .map(|status| status.is_some())
190 .map_err(storage_api_error)
191 }
192
193 pub async fn code_repository_report(
195 &self,
196 selector: CodeRepositorySelector,
197 context: RequestContext,
198 ) -> Result<CodeRepositoryReportResponse, ApiError> {
199 let store = self.store().await.map_err(storage_api_error)?;
200 let status = required_code_repository(store.as_ref(), &selector.repository).await?;
201 let report = store
202 .code_repository_report(status.repository_id.clone())
203 .await
204 .map_err(storage_api_error)?;
205 let graph_version = store
206 .current_graph_version()
207 .await
208 .map_err(storage_api_error)?;
209
210 Ok(CodeRepositoryReportResponse {
211 metadata: ApiMetadata::graph_only(&context, graph_version),
212 scope: crate::api::CodeRepositoryScopeMetadata::from_status(
213 &status,
214 &selector,
215 selector.ref_selector.clone(),
216 ),
217 report,
218 })
219 }
220}