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 #[serde(default)]
24 pub discovery_topology_hash: Option<String>,
25 #[serde(default)]
26 pub pre_snapshot_topology_hash: Option<String>,
27 #[serde(default)]
28 pub operation_metrics: DownloadOperationMetrics,
29 pub artifacts: Vec<ArtifactJournalEntry>,
30}
31
32#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
40#[serde(deny_unknown_fields)]
41pub struct DownloadOperationMetrics {
42 pub target_count: usize,
43 pub snapshot_create_started: usize,
44 pub snapshot_create_completed: usize,
45 pub snapshot_download_started: usize,
46 pub snapshot_download_completed: usize,
47 pub checksum_verify_started: usize,
48 pub checksum_verify_completed: usize,
49 pub artifact_finalize_started: usize,
50 pub artifact_finalize_completed: usize,
51}
52
53#[derive(Clone, Debug, Deserialize, Serialize)]
61#[serde(deny_unknown_fields)]
62pub struct ArtifactJournalEntry {
63 pub canister_id: String,
64 pub snapshot_id: String,
65 pub state: ArtifactState,
66 pub temp_path: Option<String>,
67 pub artifact_path: String,
68 pub checksum_algorithm: String,
69 pub checksum: Option<String>,
70 pub updated_at: String,
71}
72
73impl ArtifactJournalEntry {
74 #[must_use]
76 pub const fn resume_action(&self) -> ResumeAction {
77 match self.state {
78 ArtifactState::Created => ResumeAction::Download,
79 ArtifactState::Downloaded => ResumeAction::VerifyChecksum,
80 ArtifactState::ChecksumVerified => ResumeAction::Finalize,
81 ArtifactState::Durable => ResumeAction::Skip,
82 }
83 }
84
85 pub fn advance_to(
87 &mut self,
88 next_state: ArtifactState,
89 updated_at: String,
90 ) -> Result<(), JournalValidationError> {
91 if !self.state.can_advance_to(next_state) {
92 return Err(JournalValidationError::InvalidStateTransition {
93 from: self.state,
94 to: next_state,
95 });
96 }
97
98 self.state = next_state;
99 self.updated_at = updated_at;
100 Ok(())
101 }
102}
103
104#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
112#[serde(rename_all = "PascalCase")]
113pub enum ArtifactState {
114 Created,
115 Downloaded,
116 ChecksumVerified,
117 Durable,
118}
119
120impl ArtifactState {
121 #[must_use]
123 pub const fn can_advance_to(self, next: Self) -> bool {
124 self.as_order() <= next.as_order()
125 }
126
127 const fn as_order(self) -> u8 {
129 match self {
130 Self::Created => 0,
131 Self::Downloaded => 1,
132 Self::ChecksumVerified => 2,
133 Self::Durable => 3,
134 }
135 }
136}
137
138#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
146#[serde(rename_all = "PascalCase")]
147pub enum ResumeAction {
148 Download,
149 VerifyChecksum,
150 Finalize,
151 Skip,
152}