nako-metadata-scraper 0.1.0-alpha.2

Official Nako metadata scraper Addon Sidecar.
Documentation
use crate::providers::MetadataProvider;

use super::{
    MAX_CANDIDATES_PER_QUERY, MetadataQuery, ProviderExternalIdCapability, ProviderFieldPolicy,
    ProviderRunPolicy,
    av::facts_from_query,
    provider_execution::{
        ProviderExecutionSummary, ProviderSuggestionSet, safe_provider_failure_reason,
    },
    ranking, resolver,
};

pub(crate) async fn suggest_candidates(
    providers: &[Box<dyn MetadataProvider>],
    query: &MetadataQuery,
    external_id_capabilities: &[ProviderExternalIdCapability],
    provider_field_policy: &ProviderFieldPolicy,
    provider_run_policy: &ProviderRunPolicy,
) -> ProviderSuggestionSet {
    let mut provider_candidates = Vec::new();
    let mut execution = ProviderExecutionSummary::for_policy(provider_run_policy);
    let av_route = facts_from_query(query).map(|facts| facts.route);

    for provider in providers {
        let provider_id = provider.id().as_str();
        if provider_run_policy.disables(provider_id) {
            execution.record_suppressed(provider_id);
            continue;
        }
        if let Some(route) = av_route
            && !provider.supports_av_route(route)
        {
            execution.record_skipped_by_av_route(provider_id, route);
            continue;
        }
        if !provider_run_policy.can_select_more(execution.selected_provider_ids.len()) {
            execution.record_budget_exhausted(provider_id);
            continue;
        }

        execution.record_selected(provider_id);
        match provider.suggest(query).await {
            Ok(candidates) => {
                execution.record_returned(provider_id, candidates.len());
                provider_candidates.extend(candidates);
            }
            Err(error) => {
                let safe_failure_reason = safe_provider_failure_reason(&error);
                execution.record_failed(provider_id, safe_failure_reason);
                tracing::warn!(
                    provider = provider_id,
                    safe_failure_reason,
                    "metadata provider failed"
                );
            }
        }
    }

    let mut candidates =
        resolver::resolve_provider_candidates(provider_candidates, external_id_capabilities)
            .into_iter()
            .map(|cluster| cluster.into_ranked_candidate(query, provider_field_policy))
            .collect::<Vec<_>>();
    candidates.sort_by(ranking::compare_metadata_candidates);
    if candidates.len() > MAX_CANDIDATES_PER_QUERY {
        candidates.truncate(MAX_CANDIDATES_PER_QUERY);
    }
    ProviderSuggestionSet {
        candidates,
        execution,
    }
}