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 pub state: ArtifactState,
63 #[serde(deserialize_with = "crate::serialization::required_option")]
64 pub temp_path: Option<String>,
65 pub artifact_path: String,
66 pub checksum_algorithm: String,
67 #[serde(deserialize_with = "crate::serialization::required_option")]
68 pub checksum: Option<String>,
69 pub updated_at: String,
70}
71
72impl ArtifactJournalEntry {
73 #[must_use]
75 pub const fn resume_action(&self) -> ResumeAction {
76 match self.state {
77 ArtifactState::Created => ResumeAction::Download,
78 ArtifactState::Downloaded => ResumeAction::VerifyChecksum,
79 ArtifactState::ChecksumVerified => ResumeAction::Finalize,
80 ArtifactState::Durable => ResumeAction::Skip,
81 }
82 }
83
84 pub fn advance_to(
86 &mut self,
87 next_state: ArtifactState,
88 updated_at: String,
89 ) -> Result<(), JournalValidationError> {
90 if !self.state.can_advance_to(next_state) {
91 return Err(JournalValidationError::InvalidStateTransition {
92 from: self.state,
93 to: next_state,
94 });
95 }
96
97 self.state = next_state;
98 self.updated_at = updated_at;
99 Ok(())
100 }
101}
102
103#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
111pub enum ArtifactState {
112 Created,
113 Downloaded,
114 ChecksumVerified,
115 Durable,
116}
117
118impl ArtifactState {
119 #[must_use]
121 pub const fn can_advance_to(self, next: Self) -> bool {
122 self.as_order() <= next.as_order()
123 }
124
125 const fn as_order(self) -> u8 {
127 match self {
128 Self::Created => 0,
129 Self::Downloaded => 1,
130 Self::ChecksumVerified => 2,
131 Self::Durable => 3,
132 }
133 }
134}
135
136#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
144pub enum ResumeAction {
145 Download,
146 VerifyChecksum,
147 Finalize,
148 Skip,
149}