use crate::{
model::{
download_journal::DownloadJournalRecord,
operation_plan::OperationPlanRecord,
restore_safety::{RestoreSafetyRequirementError, RestoreSafetyRequirementRecord},
},
policy::download_integrity::{
self, DownloadIntegrityPolicyError, DurableDownloadArtifactView, DurableDownloadView,
},
};
use thiserror::Error;
#[derive(Debug)]
pub struct LocalRestoreSourceView<'a> {
restore: &'a OperationPlanRecord,
requirement: &'a RestoreSafetyRequirementRecord,
source: DurableDownloadView<'a>,
}
impl<'a> LocalRestoreSourceView<'a> {
#[must_use]
pub const fn restore(&self) -> &'a OperationPlanRecord {
self.restore
}
#[must_use]
pub const fn requirement(&self) -> &'a RestoreSafetyRequirementRecord {
self.requirement
}
#[must_use]
pub const fn source(&self) -> &DurableDownloadView<'a> {
&self.source
}
pub fn selected_artifacts(&self) -> impl Iterator<Item = &DurableDownloadArtifactView<'a>> {
self.source.artifacts().iter().filter(|artifact| {
self.restore
.selected_targets()
.binary_search_by(|target| target.as_str().cmp(artifact.artifact().canister_id()))
.is_ok()
})
}
}
pub fn validate<'a>(
restore: &'a OperationPlanRecord,
source: &'a OperationPlanRecord,
requirement: &'a RestoreSafetyRequirementRecord,
manifest: &'a DownloadJournalRecord,
) -> Result<LocalRestoreSourceView<'a>, LocalRestoreSourcePolicyError> {
requirement.validate_plans(restore, source)?;
if &manifest.digest() != requirement.source_artifacts() {
return Err(LocalRestoreSourcePolicyError::ManifestMismatch);
}
let source = download_integrity::validate(source, manifest)?;
Ok(LocalRestoreSourceView {
restore,
requirement,
source,
})
}
#[derive(Debug, Error)]
pub enum LocalRestoreSourcePolicyError {
#[error(transparent)]
Requirement(#[from] RestoreSafetyRequirementError),
#[error("local restore source manifest mismatch")]
ManifestMismatch,
#[error(transparent)]
Download(#[from] DownloadIntegrityPolicyError),
}
#[cfg(test)]
mod tests;