use super::{
NnsGovernanceAttemptReadError, NnsGovernanceCacheMetadata, NnsGovernanceCacheRequest,
read_governance_refresh_attempt_status,
};
use crate::{
HostCacheError,
cache::{
CacheCollectionCompleteness, CacheValidationStatus, validate_cache_collection_completeness,
},
cache_file::open_managed_file,
snapshot_cache::{SnapshotEnvelope, SnapshotJsonPaths},
};
use serde::Serialize;
use std::{fs::File, path::Path};
pub(in crate::nns) fn build_governance_cache_status_report<Error>(
request: &NnsGovernanceCacheRequest,
collection: crate::nns::governance::NnsGovernanceCollection,
component: &'static str,
paths: &SnapshotJsonPaths,
summary: impl FnOnce(File) -> NnsGovernanceCacheSummary,
) -> Result<NnsGovernanceCacheStatusReport, Error>
where
Error: From<HostCacheError> + From<NnsGovernanceAttemptReadError>,
{
let file = open_managed_file(&request.cache_root, &paths.snapshot_path)
.map_err(|source| HostCacheError::operation(component, source))?;
let cache = file.map(summary);
let latest_attempt = read_governance_refresh_attempt_status(
&request.cache_root,
&paths.refresh_attempt_path,
&request.network,
component,
)?;
Ok(NnsGovernanceCacheStatusReport::from_cache(
request,
collection,
paths,
cache,
latest_attempt,
))
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct NnsGovernanceCacheSummary {
pub cache_path: String,
#[serde(flatten)]
pub admission: NnsGovernanceCacheAdmission,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "cache_status", rename_all = "snake_case")]
pub enum NnsGovernanceCacheAdmission {
Valid {
#[serde(flatten)]
data: NnsGovernanceValidCacheSummary,
},
Invalid {
cache_error: String,
},
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct NnsGovernanceValidCacheSummary {
pub governance_canister_id: String,
pub point_in_time_guaranteed: bool,
pub row_count: usize,
pub page_count: u32,
pub page_size: u32,
pub fetched_at: String,
pub source_endpoint: String,
}
impl NnsGovernanceCacheSummary {
#[must_use]
pub const fn valid(&self) -> Option<&NnsGovernanceValidCacheSummary> {
match &self.admission {
NnsGovernanceCacheAdmission::Valid { data } => Some(data),
NnsGovernanceCacheAdmission::Invalid { .. } => None,
}
}
#[must_use]
pub const fn cache_status(&self) -> CacheValidationStatus {
match self.admission {
NnsGovernanceCacheAdmission::Valid { .. } => CacheValidationStatus::Valid,
NnsGovernanceCacheAdmission::Invalid { .. } => CacheValidationStatus::Invalid,
}
}
#[must_use]
pub fn cache_error(&self) -> Option<&str> {
match &self.admission {
NnsGovernanceCacheAdmission::Invalid { cache_error } => Some(cache_error),
NnsGovernanceCacheAdmission::Valid { .. } => None,
}
}
pub(in crate::nns) fn from_snapshot<Rows>(
path: &Path,
cache: &SnapshotEnvelope<NnsGovernanceCacheMetadata, Rows>,
) -> Self {
Self {
cache_path: path.display().to_string(),
admission: NnsGovernanceCacheAdmission::Valid {
data: NnsGovernanceValidCacheSummary {
governance_canister_id: cache.metadata.governance_canister_id.clone(),
point_in_time_guaranteed: cache.completeness.point_in_time_guaranteed,
row_count: cache.completeness.row_count,
page_count: cache.completeness.page_count,
page_size: cache.completeness.page_size,
fetched_at: cache.fetched_at.clone(),
source_endpoint: cache.source_endpoint.clone(),
},
},
}
}
pub(in crate::nns) fn invalid(path: &Path, reason: String) -> Self {
Self {
cache_path: path.display().to_string(),
admission: NnsGovernanceCacheAdmission::Invalid {
cache_error: reason,
},
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct NnsGovernanceCacheStatusReport {
pub schema_version: u32,
pub network: String,
pub collection: crate::nns::governance::NnsGovernanceCollection,
pub cache_root: String,
pub found: bool,
pub cache: Option<NnsGovernanceCacheSummary>,
pub expected_cache_path: String,
pub refresh_attempt_path: String,
pub latest_attempt: Option<crate::cache::SnapshotRefreshAttemptStatus>,
}
impl NnsGovernanceCacheStatusReport {
pub(in crate::nns) fn from_cache(
request: &NnsGovernanceCacheRequest,
collection: crate::nns::governance::NnsGovernanceCollection,
paths: &SnapshotJsonPaths,
cache: Option<NnsGovernanceCacheSummary>,
latest_attempt: Option<crate::cache::SnapshotRefreshAttemptStatus>,
) -> Self {
Self {
schema_version: 1,
network: request.network.clone(),
collection,
cache_root: paths
.snapshot_path
.parent()
.unwrap_or(&request.cache_root)
.display()
.to_string(),
found: cache.is_some(),
cache,
expected_cache_path: paths.snapshot_path.display().to_string(),
refresh_attempt_path: paths.refresh_attempt_path.display().to_string(),
latest_attempt,
}
}
}
pub(in crate::nns) fn validate_governance_cache_completeness(
completeness: &CacheCollectionCompleteness,
row_count: usize,
point_in_time_error: &str,
) -> Result<(), String> {
validate_cache_collection_completeness(completeness, row_count)?;
if completeness.point_in_time_guaranteed {
return Err(point_in_time_error.to_string());
}
Ok(())
}