use std::{collections::BTreeMap, path::PathBuf};
use crate::{
api::{
ApiError, ApiMetadata, CodeRepositoryFeatureFlagsResponse, CodeRepositoryImpactResponse,
CodeRepositoryIndexResponse, CodeRepositoryIndexStartResponse, CodeRepositoryQueryResponse,
CodeRepositoryRegisterRequest, CodeRepositoryRegisterResponse,
CodeRepositoryReportResponse, CodeRepositoryScopePreviewResponse,
CodeRepositoryStatusResponse, RequestContext,
},
code::{
REGISTRATION_LANGUAGE_FILTER_ERROR, build_index_snapshot_with_base_commit,
changed_paths_for_diff_with_filters, changed_paths_for_filesystem_diff,
deleted_symbol_names_for_diff, partition_changed_paths_for_selector,
prepare_full_index_plan, preview_repository_scope, register_repository,
},
domain::{
CodeFeatureFlagRequest, CodeImpactRequest, CodeIndexMode, CodeIndexRequest,
CodeIndexResourceBudget, CodeRepositorySelector, CodeRepositoryStatus,
CodeRetrievalRequest, FreshnessPolicy,
code_snapshot_expected_scope_id as expected_scope_id,
},
storage::CodeImpactChanges,
};
use crate::application::service::RelayKnowledgeService;
use super::support::{
CODE_INDEX_TASK_LEASE_MS, CODE_INDEX_TASK_MAX_ATTEMPTS, CODE_INDEX_TASK_RETRY_BACKOFF_MS,
CodeIndexTaskLeaseContext, RETAIN_RECENT_CODE_SCOPES, active_index_matches_request,
apply_code_grep_fallback, code_status_checkpoint, feature_flag_request_at_indexed_ref,
fresh_full_index_probe, index_start_from_completed, latest_compatible_code_scope_status,
now_millis, previous_index_state_for_index, recover_code_index_task_leases,
refresh_code_index_task_lease, registration_from_status, required_code_repository,
resolve_code_ref_for_selector, resolved_code_scope_status, retrieval_request_at_indexed_ref,
run_blocking_code, storage_api_error,
};
impl RelayKnowledgeService {
pub async fn register_code_repository(
&self,
request: CodeRepositoryRegisterRequest,
context: RequestContext,
) -> Result<CodeRepositoryRegisterResponse, ApiError> {
if !request.language_filters.is_empty() {
return Err(ApiError::invalid_argument(
REGISTRATION_LANGUAGE_FILTER_ERROR,
));
}
let registration = run_blocking_code(move || {
register_repository(
request.root_path,
request.alias,
request.path_filters,
request.language_filters,
)
})
.await?;
let store = self.store().await.map_err(storage_api_error)?;
let status = store
.upsert_code_repository(registration.clone())
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
Ok(CodeRepositoryRegisterResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
registration,
status,
})
}
pub async fn index_code_repository(
&self,
request: CodeIndexRequest,
context: RequestContext,
) -> Result<CodeRepositoryIndexResponse, ApiError> {
self.index_code_repository_inner(request, context, None)
.await
}
async fn index_code_repository_inner(
&self,
request: CodeIndexRequest,
context: RequestContext,
task_lease: Option<CodeIndexTaskLeaseContext>,
) -> Result<CodeRepositoryIndexResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
if let Some(response) = self
.fresh_full_index_response(&store, &status, &request, &context)
.await?
{
return Ok(response);
}
let registration = registration_from_status(&status);
let selector = request.repository.clone();
let summary = if request.mode == CodeIndexMode::Full {
self.apply_full_code_index(
&store,
registration,
selector,
CodeIndexResourceBudget::default(),
task_lease,
)
.await?
} else {
let previous = previous_index_state_for_index(&store, &status, &request).await?;
let mode = request.mode;
let snapshot = run_blocking_code(move || {
build_index_snapshot_with_base_commit(
®istration,
&selector,
mode,
previous.fingerprints,
previous.base_resolved_commit_sha,
)
})
.await?;
store
.apply_code_index_snapshot(snapshot)
.await
.map_err(storage_api_error)?
};
let status = store
.code_repository_status(summary.repository_id.clone())
.await
.map_err(storage_api_error)?
.ok_or_else(|| ApiError::storage_unavailable("code repository status is missing"))?;
let software_projection = store
.refresh_software_global_projection(summary.source_scope.clone())
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
Ok(CodeRepositoryIndexResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&status,
&request.repository,
request.repository.ref_selector.clone(),
),
summary,
status: CodeRepositoryStatus {
degraded_reason: status
.degraded_reason
.or(software_projection.status.last_error.clone()),
..status
},
})
}
async fn apply_full_code_index(
&self,
store: &std::sync::Arc<dyn crate::storage::KnowledgeStore>,
registration: crate::domain::CodeRepositoryRegistration,
selector: CodeRepositorySelector,
resource_budget: CodeIndexResourceBudget,
task_lease: Option<CodeIndexTaskLeaseContext>,
) -> Result<crate::domain::CodeIndexSummary, ApiError> {
let mut plan = run_blocking_code(move || {
prepare_full_index_plan(registration, selector, resource_budget)
})
.await?;
let session = plan.session();
store
.begin_code_index_session(session.clone())
.await
.map_err(storage_api_error)?;
loop {
refresh_code_index_task_lease(store, task_lease.as_ref()).await?;
let (next_plan, batch) = run_blocking_code(move || plan.parse_next_batch()).await?;
plan = next_plan;
let Some(batch) = batch else {
break;
};
store
.apply_code_index_batch(batch)
.await
.map_err(storage_api_error)?;
refresh_code_index_task_lease(store, task_lease.as_ref()).await?;
}
refresh_code_index_task_lease(store, task_lease.as_ref()).await?;
let summary = store
.finalize_code_index_session(session)
.await
.map_err(storage_api_error)?;
refresh_code_index_task_lease(store, task_lease.as_ref()).await?;
Ok(summary)
}
pub async fn start_code_repository_index(
&self,
request: CodeIndexRequest,
context: RequestContext,
) -> Result<CodeRepositoryIndexStartResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
if let Some(response) = self
.fresh_full_index_response(&store, &status, &request, &context)
.await?
{
return Ok(index_start_from_completed(response, None));
}
if request.mode != CodeIndexMode::Full {
let response = self.index_code_repository(request, context).await?;
return Ok(index_start_from_completed(response, None));
}
recover_code_index_task_leases(&store, now_millis()).await?;
let payload_json = serde_json::to_string(&request)
.map_err(|error| ApiError::invalid_argument(error.to_string()))?;
if let Some(active_task) = self
.active_full_index_task_for_request(&store, &status, &request, &payload_json)
.await?
{
return self
.index_start_response_from_task(
&store,
status,
active_task,
request.repository.ref_selector,
&context,
)
.await;
}
let registration = registration_from_status(&status);
let selector = request.repository.clone();
let resource_budget = CodeIndexResourceBudget::default();
let plan = run_blocking_code(move || {
prepare_full_index_plan(registration, selector, resource_budget)
})
.await?;
let session = plan.session();
let input_fingerprint = format!(
"full:{}:{}:{}",
session.repository_id, session.tree_hash, session.source_scope
);
if let Some(active_task) = store
.active_code_index_task(session.repository_id.clone())
.await
.map_err(storage_api_error)?
&& active_task.state.is_unfinished()
&& active_task.input_fingerprint == input_fingerprint
{
return self
.index_start_response_from_task(
&store,
status,
active_task,
request.repository.ref_selector,
&context,
)
.await;
}
let task = store
.queue_code_index_task(crate::storage::CodeIndexTaskSeed {
repository_id: session.repository_id.clone(),
alias: status.alias.clone(),
ref_selector: request.repository.ref_selector.clone(),
resolved_commit_sha: session.resolved_commit_sha.clone(),
tree_hash: session.tree_hash.clone(),
source_scope: session.source_scope.clone(),
path_filters: session.path_filters.clone(),
language_filters: session.language_filters.clone(),
mode: request.mode.clone(),
input_fingerprint,
resource_budget: session.resource_budget,
payload_json,
now_ms: now_millis(),
})
.await
.map_err(storage_api_error)?;
self.index_start_response_from_task(
&store,
status,
task,
request.repository.ref_selector,
&context,
)
.await
}
async fn active_full_index_task_for_request(
&self,
store: &std::sync::Arc<dyn crate::storage::KnowledgeStore>,
status: &CodeRepositoryStatus,
request: &CodeIndexRequest,
payload_json: &str,
) -> Result<Option<crate::domain::CodeIndexTaskRecord>, ApiError> {
let Some(active_task) = store
.active_code_index_task(status.repository_id.clone())
.await
.map_err(storage_api_error)?
else {
return Ok(None);
};
if !active_task.state.is_unfinished()
|| active_task.mode != CodeIndexMode::Full
|| active_task.payload_json != payload_json
{
return Ok(None);
}
let resolved = resolve_code_ref_for_selector(
status,
&request.repository,
request.repository.ref_selector.clone(),
)
.await?;
if resolved == active_task.resolved_commit_sha {
Ok(Some(active_task))
} else {
Ok(None)
}
}
async fn index_start_response_from_task(
&self,
store: &std::sync::Arc<dyn crate::storage::KnowledgeStore>,
fallback_status: CodeRepositoryStatus,
task: crate::domain::CodeIndexTaskRecord,
requested_ref: String,
context: &RequestContext,
) -> Result<CodeRepositoryIndexStartResponse, ApiError> {
let checkpoint = store
.code_index_checkpoint(task.source_scope.clone())
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
let status = store
.code_repository_status(task.repository_id.clone())
.await
.map_err(storage_api_error)?
.unwrap_or(fallback_status);
Ok(CodeRepositoryIndexStartResponse {
metadata: ApiMetadata::graph_only(context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_index_task(&task, requested_ref),
summary: None,
status,
task: Some(task),
checkpoint,
})
}
pub async fn run_code_index_task_once(
&self,
task_id: Option<String>,
context: RequestContext,
) -> Result<Option<crate::domain::CodeIndexTaskRecord>, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let lease_owner = format!("code-index-worker-{}", std::process::id());
let Some(task) = store
.claim_code_index_task(crate::storage::CodeIndexTaskClaimRequest {
task_id,
lease_owner: lease_owner.clone(),
lease_duration_ms: CODE_INDEX_TASK_LEASE_MS,
max_attempts: CODE_INDEX_TASK_MAX_ATTEMPTS,
now_ms: now_millis(),
})
.await
.map_err(storage_api_error)?
else {
return Ok(None);
};
let mut request = match serde_json::from_str::<CodeIndexRequest>(&task.payload_json) {
Ok(request) => request,
Err(error) => {
let message = format!(
"code index task '{}' payload is invalid: {error}",
task.task_id
);
let _ = store
.fail_code_index_task(crate::storage::CodeIndexTaskFailure {
task_id: task.task_id,
lease_owner,
attempt_count: task.attempt_count,
error_kind: "task_payload".to_owned(),
error_message: message.clone(),
retry_backoff_ms: CODE_INDEX_TASK_RETRY_BACKOFF_MS,
max_attempts: CODE_INDEX_TASK_MAX_ATTEMPTS,
now_ms: now_millis(),
})
.await;
return Err(ApiError::invalid_argument(message));
}
};
request.repository.ref_selector = task.resolved_commit_sha.clone();
let lease_context = CodeIndexTaskLeaseContext {
task_id: task.task_id.clone(),
lease_owner: lease_owner.clone(),
attempt_count: task.attempt_count,
lease_duration_ms: CODE_INDEX_TASK_LEASE_MS,
};
let result = self
.index_code_repository_inner(request, context, Some(lease_context.clone()))
.await;
match result {
Ok(response) => {
refresh_code_index_task_lease(&store, Some(&lease_context)).await?;
let completed = store
.complete_code_index_task(crate::storage::CodeIndexTaskCompletion {
task_id: task.task_id.clone(),
lease_owner,
attempt_count: task.attempt_count,
now_ms: now_millis(),
})
.await
.map_err(storage_api_error)?;
let _ = store
.prune_code_repository_scopes(crate::storage::CodeScopeRetentionRequest {
repository_id: response.summary.repository_id,
active_scope: response.summary.source_scope,
retain_recent_successful_scopes: RETAIN_RECENT_CODE_SCOPES,
})
.await;
Ok(Some(completed))
}
Err(error) => {
let _ = store
.fail_code_index_task(crate::storage::CodeIndexTaskFailure {
task_id: task.task_id,
lease_owner,
attempt_count: task.attempt_count,
error_kind: "code_index".to_owned(),
error_message: error.message.clone(),
retry_backoff_ms: CODE_INDEX_TASK_RETRY_BACKOFF_MS,
max_attempts: CODE_INDEX_TASK_MAX_ATTEMPTS,
now_ms: now_millis(),
})
.await;
Err(error)
}
}
}
async fn fresh_full_index_response(
&self,
store: &std::sync::Arc<dyn crate::storage::KnowledgeStore>,
status: &CodeRepositoryStatus,
request: &CodeIndexRequest,
context: &RequestContext,
) -> Result<Option<CodeRepositoryIndexResponse>, ApiError> {
if request.mode != CodeIndexMode::Full {
return Ok(None);
}
let probe = fresh_full_index_probe(status, &request.repository).await?;
let scoped_status = store
.code_repository_scope_status(
request.repository.repository.clone(),
probe.resolved_commit_sha.clone(),
probe.path_filters.clone(),
probe.language_filters.clone(),
)
.await
.map_err(storage_api_error)?;
let Some(scoped_status) = scoped_status else {
return Ok(None);
};
let expected_source_scope = expected_scope_id(
&status.repository_id,
&probe.tree_hash,
&scoped_status.path_filters,
&scoped_status.language_filters,
);
if scoped_status.stale
|| scoped_status.tree_hash.as_deref() != Some(probe.tree_hash.as_str())
|| expected_source_scope.as_deref().is_some_and(|expected| {
scoped_status.last_indexed_scope_id.as_deref() != Some(expected)
})
{
return Ok(None);
}
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
let source_scope = scoped_status
.last_indexed_scope_id
.clone()
.unwrap_or_default();
let software_projection = store
.refresh_software_global_projection(source_scope.clone())
.await
.map_err(storage_api_error)?;
let report = store
.code_repository_report(scoped_status.repository_id.clone())
.await
.map_err(storage_api_error)?;
let summary = crate::domain::CodeIndexSummary {
repository_id: scoped_status.repository_id.clone(),
source_scope,
resolved_commit_sha: probe.resolved_commit_sha,
tree_hash: probe.tree_hash,
indexed_file_count: scoped_status.indexed_file_count,
changed_path_count: 0,
skipped_unchanged_count: scoped_status.indexed_file_count,
deleted_path_count: 0,
symbol_count: scoped_status.symbol_count,
reference_count: scoped_status.reference_count,
chunk_count: scoped_status.chunk_count,
degraded_file_count: report.degraded_file_count,
progress: crate::domain::CodeIndexProgressSummary {
git_file_count: scoped_status.indexed_file_count,
blob_read_count: 0,
parsed_file_count: 0,
sqlite_write_count: 0,
skipped_file_count: scoped_status.indexed_file_count,
degraded_file_count: report.degraded_file_count,
batch_count: 0,
checkpoint_file_count: scoped_status.indexed_file_count,
resource_budget: crate::domain::CodeIndexResourceBudget::default(),
},
};
Ok(Some(CodeRepositoryIndexResponse {
metadata: ApiMetadata::graph_only(context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&scoped_status,
&request.repository,
request.repository.ref_selector.clone(),
),
summary,
status: CodeRepositoryStatus {
degraded_reason: scoped_status
.degraded_reason
.or(software_projection.status.last_error.clone()),
..scoped_status
},
}))
}
pub async fn preview_code_repository_scope(
&self,
request: CodeIndexRequest,
context: RequestContext,
) -> Result<CodeRepositoryScopePreviewResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
let registration = registration_from_status(&status);
let selector = request.repository.clone();
let preview =
run_blocking_code(move || preview_repository_scope(®istration, &selector)).await?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
Ok(CodeRepositoryScopePreviewResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&status,
&request.repository,
request.repository.ref_selector.clone(),
),
preview,
})
}
pub async fn query_code_repository(
&self,
request: CodeRetrievalRequest,
context: RequestContext,
) -> Result<CodeRepositoryQueryResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
if request.freshness_policy == FreshnessPolicy::GraphOnly {
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
return Ok(CodeRepositoryQueryResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&status,
&request.repository,
request.repository.ref_selector.clone(),
),
request,
results: Vec::new(),
degraded_reason: Some("graph_only freshness policy selected".to_owned()),
});
}
let requested_ref = request.repository.ref_selector.clone();
let mut request = retrieval_request_at_indexed_ref(request, &status).await?;
let mut served_stale_scope = false;
let mut stale_degraded_reason = None;
let scoped_status = match resolved_code_scope_status(&store, &status, &request.repository)
.await
{
Ok(scoped_status) => scoped_status,
Err(error) if request.freshness_policy == FreshnessPolicy::AllowStale => {
if !active_index_matches_request(&store, &status, &request.repository).await? {
return Err(error);
}
let Some(stale_status) =
latest_compatible_code_scope_status(&store, &request.repository).await?
else {
return Err(error);
};
let Some(last_indexed_commit) = stale_status.last_indexed_commit.clone() else {
return Err(error);
};
request.repository.ref_selector = last_indexed_commit;
served_stale_scope = true;
stale_degraded_reason = Some(
"requested ref is not indexed yet; served last completed code index".to_owned(),
);
stale_status
}
Err(error) => return Err(error),
};
if request.freshness_policy == FreshnessPolicy::WaitUntilFresh && scoped_status.stale {
return Err(ApiError::invalid_argument(format!(
"code repository '{}' scope '{}' is stale; run repo index or repo update before querying with wait_until_fresh",
scoped_status.alias,
scoped_status
.last_indexed_scope_id
.as_deref()
.unwrap_or("unscoped")
)));
}
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
let mut results = match served_stale_scope {
true => {
let source_scope =
scoped_status.last_indexed_scope_id.clone().ok_or_else(|| {
ApiError::invalid_argument(format!(
"code repository '{}' does not have an indexed source scope",
scoped_status.alias
))
})?;
store.search_code_scope(source_scope, request.clone()).await
}
false => store.search_code(request.clone()).await,
}
.map_err(storage_api_error)?;
let fallback_degraded_reason =
apply_code_grep_fallback(&store, &status, &scoped_status, &request, &mut results)
.await?;
let degraded_reason = results
.iter()
.find_map(|hit| hit.degraded_reason.clone())
.or(fallback_degraded_reason)
.or_else(|| scoped_status.degraded_reason.clone())
.or(stale_degraded_reason);
let mut scope = crate::api::CodeRepositoryScopeMetadata::from_status(
&scoped_status,
&request.repository,
requested_ref,
);
if served_stale_scope {
scope.stale = true;
}
let mut metadata = ApiMetadata::graph_only(&context, graph_version);
if served_stale_scope {
metadata.stale = true;
}
Ok(CodeRepositoryQueryResponse {
metadata,
scope,
request,
results,
degraded_reason,
})
}
pub async fn query_code_repository_feature_flags(
&self,
request: CodeFeatureFlagRequest,
context: RequestContext,
) -> Result<CodeRepositoryFeatureFlagsResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
if request.freshness_policy == FreshnessPolicy::GraphOnly {
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
return Ok(CodeRepositoryFeatureFlagsResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&status,
&request.repository,
request.repository.ref_selector.clone(),
),
request,
flags: Vec::new(),
degraded_reason: Some("graph_only freshness policy selected".to_owned()),
});
}
let requested_ref = request.repository.ref_selector.clone();
let mut request = feature_flag_request_at_indexed_ref(request, &status).await?;
let mut served_stale_scope = false;
let mut stale_degraded_reason = None;
let scoped_status = match resolved_code_scope_status(&store, &status, &request.repository)
.await
{
Ok(scoped_status) => scoped_status,
Err(error) if request.freshness_policy == FreshnessPolicy::AllowStale => {
if !active_index_matches_request(&store, &status, &request.repository).await? {
return Err(error);
}
let Some(stale_status) =
latest_compatible_code_scope_status(&store, &request.repository).await?
else {
return Err(error);
};
let Some(last_indexed_commit) = stale_status.last_indexed_commit.clone() else {
return Err(error);
};
request.repository.ref_selector = last_indexed_commit;
served_stale_scope = true;
stale_degraded_reason = Some(
"requested ref is not indexed yet; served last completed code index".to_owned(),
);
stale_status
}
Err(error) => return Err(error),
};
if request.freshness_policy == FreshnessPolicy::WaitUntilFresh && scoped_status.stale {
return Err(ApiError::invalid_argument(format!(
"code repository '{}' scope '{}' is stale; run repo index or repo update before querying feature flags with wait_until_fresh",
scoped_status.alias,
scoped_status
.last_indexed_scope_id
.as_deref()
.unwrap_or("unscoped")
)));
}
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
let flags = match served_stale_scope {
true => {
let source_scope =
scoped_status.last_indexed_scope_id.clone().ok_or_else(|| {
ApiError::invalid_argument(format!(
"code repository '{}' does not have an indexed source scope",
scoped_status.alias
))
})?;
store
.search_code_feature_flags_scope(source_scope, request.clone())
.await
}
false => store.search_code_feature_flags(request.clone()).await,
}
.map_err(storage_api_error)?;
let mut scope = crate::api::CodeRepositoryScopeMetadata::from_status(
&scoped_status,
&request.repository,
requested_ref,
);
if served_stale_scope {
scope.stale = true;
}
let degraded_reason = scoped_status
.degraded_reason
.clone()
.or(stale_degraded_reason);
let mut metadata = ApiMetadata::graph_only(&context, graph_version);
if served_stale_scope {
metadata.stale = true;
}
Ok(CodeRepositoryFeatureFlagsResponse {
metadata,
scope,
request,
flags,
degraded_reason,
})
}
pub async fn impact_code_repository(
&self,
mut request: CodeImpactRequest,
context: RequestContext,
) -> Result<CodeRepositoryImpactResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &request.repository.repository).await?;
let head_commit =
resolve_code_ref_for_selector(&status, &request.repository, request.head_ref.clone())
.await?;
request.repository.ref_selector = head_commit.clone();
let scoped_status =
resolved_code_scope_status(&store, &status, &request.repository).await?;
let root = PathBuf::from(status.root_path.clone());
let base_ref = request.base_ref.clone();
let head_ref = head_commit.clone();
let path_filters = scoped_status.path_filters.clone();
let language_filters = scoped_status.language_filters.clone();
let base_fingerprints = if base_ref.starts_with("filesystem:") {
let mut base_selector = request.repository.clone();
base_selector.ref_selector = base_ref.clone();
match resolved_code_scope_status(&store, &status, &base_selector).await {
Ok(base_status) => match base_status.last_indexed_scope_id {
Some(source_scope) => Some(
store
.code_file_fingerprints_for_scope(source_scope)
.await
.map_err(storage_api_error)?,
),
None => None,
},
Err(_) => None,
}
} else {
None
};
let changed_paths = if let Some(base_fingerprints) = base_fingerprints {
run_blocking_code(move || {
let previous_hashes = base_fingerprints
.into_iter()
.map(|file| (file.path, file.blob_hash))
.collect::<BTreeMap<_, _>>();
changed_paths_for_filesystem_diff(
&root,
&head_ref,
&path_filters,
&language_filters,
&previous_hashes,
)
})
.await?
} else {
run_blocking_code(move || {
changed_paths_for_diff_with_filters(
root,
&base_ref,
&head_ref,
&path_filters,
&language_filters,
)
})
.await?
};
let registration = registration_from_status(&status);
let path_groups = {
let registration = registration.clone();
let selector = request.repository.clone();
let changed_paths = changed_paths.clone();
run_blocking_code(move || {
partition_changed_paths_for_selector(®istration, &selector, changed_paths)
})
.await?
};
let selector = request.repository.clone();
let base_ref = request.base_ref.clone();
let head_ref = head_commit;
let deleted_symbol_names = run_blocking_code(move || {
deleted_symbol_names_for_diff(®istration, &selector, &base_ref, &head_ref)
})
.await?;
let results = store
.analyze_code_impact(
request.clone(),
CodeImpactChanges {
paths: changed_paths.clone(),
deleted_symbol_names,
},
)
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
let scope = crate::api::CodeRepositoryScopeMetadata::from_status(
&scoped_status,
&request.repository,
request.head_ref.clone(),
);
Ok(CodeRepositoryImpactResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope,
request,
path_groups,
results,
})
}
pub async fn code_repository_status(
&self,
selector: CodeRepositorySelector,
context: RequestContext,
) -> Result<CodeRepositoryStatusResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &selector.repository).await?;
recover_code_index_task_leases(&store, now_millis()).await?;
let active_task = store
.active_code_index_task(status.repository_id.clone())
.await
.map_err(storage_api_error)?;
let checkpoint = code_status_checkpoint(&store, &status, active_task.as_ref()).await?;
let retention = store
.code_scope_retention(status.repository_id.clone())
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
Ok(CodeRepositoryStatusResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
status,
active_task,
checkpoint,
retention,
})
}
pub(crate) async fn code_repository_is_registered(
&self,
repository: String,
) -> Result<bool, ApiError> {
let selector = CodeRepositorySelector::new(repository, "HEAD", Vec::new(), Vec::new())
.map_err(|error| ApiError::invalid_argument(error.to_string()))?;
let store = self.store().await.map_err(storage_api_error)?;
store
.code_repository_status(selector.repository)
.await
.map(|status| status.is_some())
.map_err(storage_api_error)
}
pub async fn code_repository_report(
&self,
selector: CodeRepositorySelector,
context: RequestContext,
) -> Result<CodeRepositoryReportResponse, ApiError> {
let store = self.store().await.map_err(storage_api_error)?;
let status = required_code_repository(&store, &selector.repository).await?;
let report = store
.code_repository_report(status.repository_id.clone())
.await
.map_err(storage_api_error)?;
let graph_version = store
.current_graph_version()
.await
.map_err(storage_api_error)?;
Ok(CodeRepositoryReportResponse {
metadata: ApiMetadata::graph_only(&context, graph_version),
scope: crate::api::CodeRepositoryScopeMetadata::from_status(
&status,
&selector,
selector.ref_selector.clone(),
),
report,
})
}
}