Skip to main content

canic_backup/persistence/
error.rs

1//! Module: persistence::error
2//!
3//! Responsibility: define typed persistence and integrity failures.
4//! Does not own: filesystem operations, JSON encoding, or checksum calculation.
5//! Boundary: aggregates lower-layer errors returned by persistence APIs.
6
7use crate::{
8    artifacts::ArtifactChecksumError,
9    execution::BackupExecutionJournalError,
10    journal::{ArtifactState, JournalValidationError},
11    manifest::ManifestValidationError,
12    plan::BackupPlanError,
13};
14
15use std::io;
16
17use thiserror::Error as ThisError;
18
19///
20/// PersistenceError
21///
22/// Typed persistence and integrity failure for backup layout operations.
23/// Owned by persistence and returned by backup file IO and verification APIs.
24///
25
26#[derive(Debug, ThisError)]
27pub enum PersistenceError {
28    #[error("restore reference conflicts with retained recovery authority: {path}")]
29    RestoreReferenceConflict { path: String },
30
31    #[error("invalid restore reference record: {path}")]
32    InvalidRestoreReferences { path: String },
33
34    /// Canonical IC Backup directory-publication and integrity refusal.
35    #[error(transparent)]
36    ArtifactCommit(#[from] ic_backup::ops::persistence::PersistenceError),
37
38    #[error("artifact path escapes backup root: {artifact_path}")]
39    ArtifactPathEscapesBackup { artifact_path: String },
40
41    #[error("manifest backup id {manifest} does not match journal backup id {journal}")]
42    BackupIdMismatch { manifest: String, journal: String },
43
44    #[error(transparent)]
45    Checksum(#[from] ArtifactChecksumError),
46
47    #[error("backup execution operation {sequence} is {state} but has no matching receipt")]
48    ExecutionOperationMissingReceipt { sequence: usize, state: String },
49
50    #[error("backup execution operation {sequence} timestamp does not match latest receipt")]
51    ExecutionOperationReceiptTimestampMismatch { sequence: usize },
52
53    #[error(transparent)]
54    InvalidBackupPlan(#[from] BackupPlanError),
55
56    #[error(transparent)]
57    InvalidExecutionJournal(#[from] BackupExecutionJournalError),
58
59    #[error(transparent)]
60    InvalidJournal(#[from] JournalValidationError),
61
62    #[error(transparent)]
63    InvalidManifest(#[from] ManifestValidationError),
64
65    #[error(transparent)]
66    Io(#[from] io::Error),
67
68    #[error(transparent)]
69    Json(#[from] serde_json::Error),
70
71    #[error("manifest topology receipt {field} does not match journal topology receipt")]
72    ManifestJournalTopologyReceiptMismatch {
73        field: String,
74        manifest: String,
75        journal: String,
76    },
77
78    #[error("backup manifest conflicts with the existing canonical manifest: {path}")]
79    ManifestConflict { path: String },
80
81    #[error(
82        "manifest artifact path for {canister_id} snapshot {snapshot_id} does not match journal artifact path"
83    )]
84    ManifestJournalArtifactPathMismatch {
85        canister_id: String,
86        snapshot_id: String,
87        manifest: String,
88        journal: String,
89    },
90
91    #[error(
92        "manifest checksum for {canister_id} snapshot {snapshot_id} does not match journal checksum"
93    )]
94    ManifestJournalChecksumMismatch {
95        canister_id: String,
96        snapshot_id: String,
97        manifest: String,
98        journal: String,
99    },
100
101    #[error("artifact path does not exist: {0}")]
102    MissingArtifact(String),
103
104    #[error("manifest member {canister_id} snapshot {snapshot_id} has no journal artifact")]
105    MissingJournalArtifact {
106        canister_id: String,
107        snapshot_id: String,
108    },
109
110    #[error("journal artifact {canister_id} snapshot {snapshot_id} has no checksum")]
111    MissingJournalArtifactChecksum {
112        canister_id: String,
113        snapshot_id: String,
114    },
115
116    #[error(
117        "journal artifact {canister_id} snapshot {snapshot_id} must be checksum verified before durable publication; found {state:?}"
118    )]
119    ArtifactNotChecksumVerified {
120        canister_id: String,
121        snapshot_id: String,
122        state: ArtifactState,
123    },
124
125    #[error("journal artifact {canister_id} snapshot {snapshot_id} is not durable")]
126    NonDurableArtifact {
127        canister_id: String,
128        snapshot_id: String,
129    },
130
131    #[error("backup plan {field} does not match execution journal")]
132    PlanJournalMismatch {
133        field: &'static str,
134        plan: String,
135        journal: String,
136    },
137
138    #[error("backup plan operation {sequence} {field} does not match execution journal")]
139    PlanJournalOperationMismatch {
140        sequence: usize,
141        field: &'static str,
142        plan: String,
143        journal: String,
144    },
145
146    #[error("journal artifact {canister_id} snapshot {snapshot_id} is not declared in manifest")]
147    UnexpectedJournalArtifact {
148        canister_id: String,
149        snapshot_id: String,
150    },
151}