canic_backup/journal/
types.rs1use crate::journal::JournalValidationError;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Clone, Debug, Deserialize, Serialize)]
19#[serde(deny_unknown_fields)]
20pub struct DownloadJournal {
21 pub journal_version: u16,
22 pub backup_id: String,
23 pub discovery_topology_hash: String,
24 pub pre_snapshot_topology_hash: String,
25 pub operation_metrics: DownloadOperationMetrics,
26 pub artifacts: Vec<ArtifactJournalEntry>,
27}
28
29#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
37#[serde(deny_unknown_fields)]
38pub struct DownloadOperationMetrics {
39 pub target_count: usize,
40 pub snapshot_create_started: usize,
41 pub snapshot_create_completed: usize,
42 pub snapshot_download_started: usize,
43 pub snapshot_download_completed: usize,
44 pub checksum_verify_started: usize,
45 pub checksum_verify_completed: usize,
46 pub artifact_finalize_started: usize,
47 pub artifact_finalize_completed: usize,
48}
49
50#[derive(Clone, Debug, Deserialize, Serialize)]
58#[serde(deny_unknown_fields)]
59pub struct ArtifactJournalEntry {
60 pub canister_id: String,
61 pub snapshot_id: String,
62 #[serde(deserialize_with = "crate::serialization::required_option")]
63 pub snapshot_taken_at_timestamp: Option<u64>,
64 #[serde(deserialize_with = "crate::serialization::required_option")]
65 pub snapshot_total_size_bytes: Option<u64>,
66 pub state: ArtifactState,
67 #[serde(deserialize_with = "crate::serialization::required_option")]
68 pub temp_path: Option<String>,
69 pub artifact_path: String,
70 pub checksum_algorithm: String,
71 #[serde(deserialize_with = "crate::serialization::required_option")]
72 pub checksum: Option<String>,
73 pub updated_at: String,
74}
75
76impl ArtifactJournalEntry {
77 #[must_use]
79 pub const fn resume_action(&self) -> ResumeAction {
80 match self.state {
81 ArtifactState::Created => ResumeAction::Download,
82 ArtifactState::Downloaded => ResumeAction::VerifyChecksum,
83 ArtifactState::ChecksumVerified => ResumeAction::Finalize,
84 ArtifactState::Durable => ResumeAction::Skip,
85 }
86 }
87
88 pub fn advance_to(
90 &mut self,
91 next_state: ArtifactState,
92 updated_at: String,
93 ) -> Result<(), JournalValidationError> {
94 if !self.state.can_advance_to(next_state) {
95 return Err(JournalValidationError::InvalidStateTransition {
96 from: self.state,
97 to: next_state,
98 });
99 }
100
101 self.state = next_state;
102 self.updated_at = updated_at;
103 Ok(())
104 }
105}
106
107#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
115pub enum ArtifactState {
116 Created,
117 Downloaded,
118 ChecksumVerified,
119 Durable,
120}
121
122impl ArtifactState {
123 #[must_use]
125 pub const fn can_advance_to(self, next: Self) -> bool {
126 matches!(
127 (self, next),
128 (Self::Created, Self::Downloaded)
129 | (Self::Downloaded, Self::ChecksumVerified)
130 | (Self::ChecksumVerified, Self::Durable)
131 )
132 }
133}
134
135#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
143pub enum ResumeAction {
144 Download,
145 VerifyChecksum,
146 Finalize,
147 Skip,
148}