frigg 0.3.2

Local-first MCP server for code understanding.
Documentation
use super::*;
use crate::mcp::server::runtime_cache::serialized_value_estimated_bytes;

impl FriggMcpServer {
    pub(super) fn invalidate_repository_navigation_response_caches(&self, repository_id: &str) {
        let mut go_to_definition_cache = self
            .cache_state
            .go_to_definition_response_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let before = go_to_definition_cache.len();
        go_to_definition_cache.retain(|key, _| {
            !response_cache_scopes_include_repository(
                repository_id,
                &key.scoped_repository_ids,
                &key.freshness_scopes,
            )
        });
        self.record_runtime_cache_event(
            RuntimeCacheFamily::GoToDefinitionResponse,
            RuntimeCacheEvent::Invalidation,
            before.saturating_sub(go_to_definition_cache.len()),
        );

        let mut find_declarations_cache = self
            .cache_state
            .find_declarations_response_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let before = find_declarations_cache.len();
        find_declarations_cache.retain(|key, _| {
            !response_cache_scopes_include_repository(
                repository_id,
                &key.scoped_repository_ids,
                &key.freshness_scopes,
            )
        });
        self.record_runtime_cache_event(
            RuntimeCacheFamily::FindDeclarationsResponse,
            RuntimeCacheEvent::Invalidation,
            before.saturating_sub(find_declarations_cache.len()),
        );

        let mut heuristic_reference_cache = self
            .cache_state
            .heuristic_reference_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let before = heuristic_reference_cache.len();
        heuristic_reference_cache.retain(|key, _| key.repository_id != repository_id);
        self.record_runtime_cache_event(
            RuntimeCacheFamily::HeuristicReference,
            RuntimeCacheEvent::Invalidation,
            before.saturating_sub(heuristic_reference_cache.len()),
        );
    }

    pub(super) fn cached_go_to_definition_response(
        &self,
        cache_key: &GoToDefinitionResponseCacheKey,
    ) -> Option<CachedGoToDefinitionResponse> {
        let cached = self
            .cache_state
            .go_to_definition_response_cache
            .read()
            .unwrap_or_else(|poisoned| poisoned.into_inner())
            .get(cache_key)
            .cloned();
        self.record_runtime_cache_event(
            RuntimeCacheFamily::GoToDefinitionResponse,
            if cached.is_some() {
                RuntimeCacheEvent::Hit
            } else {
                RuntimeCacheEvent::Miss
            },
            1,
        );
        cached
    }

    #[allow(clippy::too_many_arguments)]
    pub(super) fn cache_go_to_definition_response(
        &self,
        cache_key: GoToDefinitionResponseCacheKey,
        response: &GoToDefinitionResponse,
        scoped_repository_ids: &[String],
        selected_symbol_id: Option<&str>,
        selected_precise_symbol: Option<&str>,
        resolution_precision: Option<&str>,
        resolution_source: Option<&str>,
        effective_limit: usize,
        precise_artifacts_ingested: usize,
        precise_artifacts_failed: usize,
        match_count: usize,
    ) {
        let mut cache = self
            .cache_state
            .go_to_definition_response_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let inserted = cache
            .insert(
                cache_key,
                CachedGoToDefinitionResponse {
                    response: response.clone(),
                    scoped_repository_ids: scoped_repository_ids.to_owned(),
                    selected_symbol_id: selected_symbol_id.map(str::to_owned),
                    selected_precise_symbol: selected_precise_symbol.map(str::to_owned),
                    resolution_precision: resolution_precision.map(str::to_owned),
                    resolution_source: resolution_source.map(str::to_owned),
                    effective_limit,
                    precise_artifacts_ingested,
                    precise_artifacts_failed,
                    match_count,
                },
            )
            .is_none();
        if inserted {
            self.record_runtime_cache_event(
                RuntimeCacheFamily::GoToDefinitionResponse,
                RuntimeCacheEvent::Insert,
                1,
            );
        }
        self.trim_runtime_cache_to_budget(
            RuntimeCacheFamily::GoToDefinitionResponse,
            &mut cache,
            |_, entry| {
                serialized_value_estimated_bytes(&entry.response)
                    .saturating_add(
                        entry
                            .scoped_repository_ids
                            .iter()
                            .map(String::len)
                            .sum::<usize>(),
                    )
                    .saturating_add(entry.selected_symbol_id.as_ref().map_or(0, String::len))
                    .saturating_add(
                        entry
                            .selected_precise_symbol
                            .as_ref()
                            .map_or(0, String::len),
                    )
            },
        );
    }

    pub(super) fn cached_find_declarations_response(
        &self,
        cache_key: &FindDeclarationsResponseCacheKey,
    ) -> Option<CachedFindDeclarationsResponse> {
        let cached = self
            .cache_state
            .find_declarations_response_cache
            .read()
            .unwrap_or_else(|poisoned| poisoned.into_inner())
            .get(cache_key)
            .cloned();
        self.record_runtime_cache_event(
            RuntimeCacheFamily::FindDeclarationsResponse,
            if cached.is_some() {
                RuntimeCacheEvent::Hit
            } else {
                RuntimeCacheEvent::Miss
            },
            1,
        );
        cached
    }

    #[allow(clippy::too_many_arguments)]
    pub(super) fn cache_find_declarations_response(
        &self,
        cache_key: FindDeclarationsResponseCacheKey,
        response: &FindDeclarationsResponse,
        scoped_repository_ids: &[String],
        selected_symbol_id: Option<&str>,
        selected_precise_symbol: Option<&str>,
        resolution_precision: Option<&str>,
        resolution_source: Option<&str>,
        effective_limit: usize,
        precise_artifacts_ingested: usize,
        precise_artifacts_failed: usize,
        match_count: usize,
    ) {
        let mut cache = self
            .cache_state
            .find_declarations_response_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let inserted = cache
            .insert(
                cache_key,
                CachedFindDeclarationsResponse {
                    response: response.clone(),
                    scoped_repository_ids: scoped_repository_ids.to_owned(),
                    selected_symbol_id: selected_symbol_id.map(str::to_owned),
                    selected_precise_symbol: selected_precise_symbol.map(str::to_owned),
                    resolution_precision: resolution_precision.map(str::to_owned),
                    resolution_source: resolution_source.map(str::to_owned),
                    effective_limit,
                    precise_artifacts_ingested,
                    precise_artifacts_failed,
                    match_count,
                },
            )
            .is_none();
        if inserted {
            self.record_runtime_cache_event(
                RuntimeCacheFamily::FindDeclarationsResponse,
                RuntimeCacheEvent::Insert,
                1,
            );
        }
        self.trim_runtime_cache_to_budget(
            RuntimeCacheFamily::FindDeclarationsResponse,
            &mut cache,
            |_, entry| {
                serialized_value_estimated_bytes(&entry.response)
                    .saturating_add(
                        entry
                            .scoped_repository_ids
                            .iter()
                            .map(String::len)
                            .sum::<usize>(),
                    )
                    .saturating_add(entry.selected_symbol_id.as_ref().map_or(0, String::len))
                    .saturating_add(
                        entry
                            .selected_precise_symbol
                            .as_ref()
                            .map_or(0, String::len),
                    )
            },
        );
    }

    pub(super) fn cached_heuristic_references(
        &self,
        cache_key: &HeuristicReferenceCacheKey,
    ) -> Option<CachedHeuristicReferences> {
        let cached = self
            .cache_state
            .heuristic_reference_cache
            .read()
            .unwrap_or_else(|poisoned| poisoned.into_inner())
            .get(cache_key)
            .cloned();
        self.record_runtime_cache_event(
            RuntimeCacheFamily::HeuristicReference,
            if cached.is_some() {
                RuntimeCacheEvent::Hit
            } else {
                RuntimeCacheEvent::Miss
            },
            1,
        );
        cached
    }

    pub(super) fn cache_heuristic_references(
        &self,
        cache_key: HeuristicReferenceCacheKey,
        references: Vec<HeuristicReference>,
        source_files_discovered: usize,
        source_read_diagnostics_count: usize,
        source_files_loaded: usize,
        source_bytes_loaded: u64,
    ) {
        let mut cache = self
            .cache_state
            .heuristic_reference_cache
            .write()
            .unwrap_or_else(|poisoned| poisoned.into_inner());
        let inserted = cache
            .insert(
                cache_key,
                CachedHeuristicReferences {
                    references: Arc::new(references),
                    source_files_discovered,
                    source_read_diagnostics_count,
                    source_files_loaded,
                    source_bytes_loaded,
                },
            )
            .is_none();
        if inserted {
            self.record_runtime_cache_event(
                RuntimeCacheFamily::HeuristicReference,
                RuntimeCacheEvent::Insert,
                1,
            );
        }
        self.trim_runtime_cache_to_budget(
            RuntimeCacheFamily::HeuristicReference,
            &mut cache,
            |_, entry| {
                serialized_value_estimated_bytes(entry.references.as_ref())
                    .saturating_add(entry.source_files_loaded.saturating_mul(32))
            },
        );
    }
}