use super::{
SubnetCatalogFilters, SubnetCatalogListReport, SubnetCatalogListRequest,
SubnetCatalogSubnetRow, load_catalog_for_report,
};
use crate::QueryProgress;
use crate::{
nns::LiveNnsSource,
subnet_catalog::{
RoutingRange, SUBNET_CATALOG_LIST_REPORT_SCHEMA_VERSION, SubnetCatalogHostError,
SubnetCatalogSource, SubnetInfo,
},
};
use std::collections::BTreeMap;
pub async fn build_subnet_catalog_list_report(
request: &SubnetCatalogListRequest,
) -> Result<SubnetCatalogListReport, SubnetCatalogHostError> {
build_subnet_catalog_list_report_with_source_and_progress(
request,
&LiveNnsSource::default(),
&mut |_| {},
)
.await
}
pub async fn build_subnet_catalog_list_report_with_source_and_progress(
request: &SubnetCatalogListRequest,
source: &dyn SubnetCatalogSource,
progress: &mut (dyn QueryProgress + Send),
) -> Result<SubnetCatalogListReport, SubnetCatalogHostError> {
let (cached, stale) = load_catalog_for_report(
&request.cache,
&request.read_policy,
request.now_unix_secs,
request.stale_after_seconds,
source,
progress,
)
.await?;
let subnets = cached
.catalog
.subnets()
.iter()
.filter(|subnet| subnet_matches_filters(subnet, request.filters))
.collect::<Vec<_>>();
let mut ranges_by_subnet = subnets
.iter()
.map(|subnet| (subnet.subnet_principal.as_str(), (0_usize, Vec::new())))
.collect::<BTreeMap<_, _>>();
for range in &cached.catalog.raw().routing_ranges {
if let Some((count, shown)) = ranges_by_subnet.get_mut(range.subnet_principal.as_str()) {
if request.show_ranges
&& *count >= request.range_offset
&& shown.len() < request.range_limit
{
shown.push(range.clone());
}
*count += 1;
}
}
let subnets = subnets
.into_iter()
.map(|subnet| {
let (count, ranges) = ranges_by_subnet
.remove(subnet.subnet_principal.as_str())
.unwrap_or_default();
subnet_row(subnet, request, count, ranges)
})
.collect();
Ok(SubnetCatalogListReport {
metadata: super::SubnetCatalogReportMetadata::from_catalog(&cached.catalog),
cache_disposition: cached.disposition,
catalog_schema_version: cached.catalog.raw().catalog_schema_version,
catalog_path: cached.path.display().to_string(),
catalog_stale: stale.catalog_stale,
stale_reason: stale.stale_reason,
schema_version: SUBNET_CATALOG_LIST_REPORT_SCHEMA_VERSION,
network: cached.catalog.provenance().network.clone(),
subnets,
})
}
fn subnet_matches_filters(subnet: &SubnetInfo, filters: SubnetCatalogFilters) -> bool {
filters
.kind
.is_none_or(|kind| subnet.classification.subnet_kind == kind)
&& filters.specialization.is_none_or(|specialization| {
subnet.classification.subnet_specialization == specialization
})
&& filters
.geographic_scope
.is_none_or(|scope| subnet.classification.geographic_scope == scope)
}
fn subnet_row(
subnet: &SubnetInfo,
request: &SubnetCatalogListRequest,
range_count: usize,
shown_ranges: Vec<RoutingRange>,
) -> SubnetCatalogSubnetRow {
SubnetCatalogSubnetRow {
classification: subnet.classification.clone(),
subnet_principal: subnet.subnet_principal.clone(),
node_count: subnet.node_count,
charges_apply_by_default: subnet.charges_apply_by_default,
range_count,
ranges_shown: shown_ranges.len(),
range_offset: request.range_offset,
range_limit: request.range_limit,
ranges: shown_ranges,
}
}