Skip to main content

ic_backup/model/download_journal/view/
mod.rs

1//! Read-only resume projections; no scheduling, IO or transitions.
2
3use super::{ArtifactStateRecord, DownloadJournalRecord};
4use serde::Serialize;
5
6/// Next local action derived from retained artifact state.
7#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
8pub enum ResumeAction {
9    /// Transfer is not locally complete; backend-specific recovery must decide dispatch.
10    Download,
11    /// Verify complete staged bytes and retain their checksum.
12    VerifyChecksum,
13    /// Publish the checksum-bound staging directory or adopt its matching canonical tree.
14    Finalize,
15    /// Local publication is retained; this is not fresh byte or remote verification.
16    Skip,
17}
18
19/// Owned read-only projection for one retained artifact.
20#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
21pub struct DownloadArtifactView {
22    /// Normalized source principal.
23    pub canister_id: String,
24    /// Exact backend snapshot token.
25    pub snapshot_id: String,
26    /// Retained local state.
27    pub state: ArtifactStateRecord,
28    /// Next local action, never automatic remote retry permission.
29    pub resume_action: ResumeAction,
30}
31
32/// Owned read-only journal projection with progress derived from actual entries.
33#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
34pub struct DownloadJournalView {
35    /// Canonical immutable intent digest.
36    pub intent: String,
37    /// Number of artifacts not yet locally durable.
38    pub pending_artifacts: usize,
39    /// Whether every selected artifact has retained durable publication.
40    pub is_complete: bool,
41    /// Entry-derived resume actions in canonical principal order.
42    pub artifacts: Vec<DownloadArtifactView>,
43}
44
45impl DownloadJournalRecord {
46    /// Project retained local progress without observing bytes or remote state.
47    #[must_use]
48    pub fn resume_view(&self) -> DownloadJournalView {
49        let artifacts: Vec<_> = self
50            .artifacts()
51            .iter()
52            .map(|artifact| DownloadArtifactView {
53                canister_id: artifact.canister_id().to_owned(),
54                snapshot_id: artifact.snapshot_id().to_owned(),
55                state: artifact.state(),
56                resume_action: match artifact.state() {
57                    ArtifactStateRecord::Created => ResumeAction::Download,
58                    ArtifactStateRecord::Downloaded => ResumeAction::VerifyChecksum,
59                    ArtifactStateRecord::ChecksumVerified => ResumeAction::Finalize,
60                    ArtifactStateRecord::Durable => ResumeAction::Skip,
61                },
62            })
63            .collect();
64        let pending_artifacts = artifacts
65            .iter()
66            .filter(|entry| entry.state != ArtifactStateRecord::Durable)
67            .count();
68        DownloadJournalView {
69            intent: self.intent().to_owned(),
70            pending_artifacts,
71            is_complete: pending_artifacts == 0,
72            artifacts,
73        }
74    }
75}