Skip to main content

canic_backup/journal/
report.rs

1//! Module: journal::report
2//!
3//! Responsibility: project durable journal state into resumability reports.
4//! Does not own: journal validation, artifact state mutation, or persistence.
5//! Boundary: reads journal records and returns read-only reporting projections.
6
7use crate::journal::{ArtifactState, DownloadJournal, DownloadOperationMetrics, ResumeAction};
8
9use serde::{Deserialize, Serialize};
10
11impl DownloadJournal {
12    /// Build a resumability report from the current journal state.
13    #[must_use]
14    pub fn resume_report(&self) -> JournalResumeReport {
15        let mut counts = JournalStateCounts::default();
16        let mut artifacts = Vec::with_capacity(self.artifacts.len());
17
18        for artifact in &self.artifacts {
19            counts.record(artifact.state, artifact.resume_action());
20            artifacts.push(ArtifactResumeReport {
21                canister_id: artifact.canister_id.clone(),
22                snapshot_id: artifact.snapshot_id.clone(),
23                state: artifact.state,
24                resume_action: artifact.resume_action(),
25                artifact_path: artifact.artifact_path.clone(),
26                temp_path: artifact.temp_path.clone(),
27                updated_at: artifact.updated_at.clone(),
28            });
29        }
30
31        JournalResumeReport {
32            backup_id: self.backup_id.clone(),
33            discovery_topology_hash: self.discovery_topology_hash.clone(),
34            pre_snapshot_topology_hash: self.pre_snapshot_topology_hash.clone(),
35            total_artifacts: self.artifacts.len(),
36            is_complete: counts.skip == self.artifacts.len(),
37            pending_artifacts: self.artifacts.len() - counts.skip,
38            counts,
39            operation_metrics: self.operation_metrics.clone(),
40            artifacts,
41        }
42    }
43}
44
45///
46/// JournalResumeReport
47///
48/// Read-only resume projection of one download journal.
49/// Owned by backup journaling and consumed by status/reporting surfaces.
50///
51
52#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
53pub struct JournalResumeReport {
54    pub backup_id: String,
55    pub discovery_topology_hash: Option<String>,
56    pub pre_snapshot_topology_hash: Option<String>,
57    pub total_artifacts: usize,
58    pub is_complete: bool,
59    pub pending_artifacts: usize,
60    pub counts: JournalStateCounts,
61    pub operation_metrics: DownloadOperationMetrics,
62    pub artifacts: Vec<ArtifactResumeReport>,
63}
64
65///
66/// JournalStateCounts
67///
68/// Aggregated artifact state and resume-action counters.
69/// Owned by backup journaling and embedded in resume reports.
70///
71
72#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
73pub struct JournalStateCounts {
74    pub created: usize,
75    pub downloaded: usize,
76    pub checksum_verified: usize,
77    pub durable: usize,
78    pub download: usize,
79    pub verify_checksum: usize,
80    pub finalize: usize,
81    pub skip: usize,
82}
83
84impl JournalStateCounts {
85    const fn record(&mut self, state: ArtifactState, action: ResumeAction) {
86        match state {
87            ArtifactState::Created => self.created += 1,
88            ArtifactState::Downloaded => self.downloaded += 1,
89            ArtifactState::ChecksumVerified => self.checksum_verified += 1,
90            ArtifactState::Durable => self.durable += 1,
91        }
92
93        match action {
94            ResumeAction::Download => self.download += 1,
95            ResumeAction::VerifyChecksum => self.verify_checksum += 1,
96            ResumeAction::Finalize => self.finalize += 1,
97            ResumeAction::Skip => self.skip += 1,
98        }
99    }
100}
101
102///
103/// ArtifactResumeReport
104///
105/// Read-only resume projection for one artifact journal entry.
106/// Owned by backup journaling and embedded in resume reports.
107///
108
109#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
110pub struct ArtifactResumeReport {
111    pub canister_id: String,
112    pub snapshot_id: String,
113    pub state: ArtifactState,
114    pub resume_action: ResumeAction,
115    pub artifact_path: String,
116    pub temp_path: Option<String>,
117    pub updated_at: String,
118}