Skip to main content

dpp_domain/ports/ghosts/
backup.rs

1//! [`GhostBackup`] — a no-op back-up copy for development and standalone nodes.
2
3use async_trait::async_trait;
4use chrono::Utc;
5use uuid::Uuid;
6
7use crate::error::dpp::DppError;
8use crate::passport::{Passport, PassportId};
9use crate::ports::backup::{
10    BackupCopyPort, BackupReceipt, BackupStatus, BackupVerification, retention_deadline,
11};
12
13/// No-op back-up copy for development and standalone deployments.
14///
15/// All operations succeed without performing any I/O. Returns synthetic
16/// receipts with `backup_id = "GHOST-{uuid}"`. Use in tests and wherever no
17/// provider is configured.
18pub struct GhostBackup;
19
20#[async_trait]
21impl BackupCopyPort for GhostBackup {
22    async fn store(
23        &self,
24        passport: &Passport,
25        retention_years: u32,
26    ) -> Result<BackupReceipt, DppError> {
27        let now = Utc::now();
28        Ok(BackupReceipt {
29            backup_id: format!("GHOST-{}", Uuid::now_v7()),
30            passport_id: passport.id,
31            content_hash: String::new(),
32            stored_at: now,
33            retention_until: retention_deadline(now, retention_years),
34        })
35    }
36
37    async fn update(&self, passport: &Passport) -> Result<BackupReceipt, DppError> {
38        let now = Utc::now();
39        Ok(BackupReceipt {
40            backup_id: format!("GHOST-{}", Uuid::now_v7()),
41            passport_id: passport.id,
42            content_hash: String::new(),
43            stored_at: now,
44            // `update` has no `retention_years` parameter (see
45            // `BackupCopyPort` trait) so the general 10-year default is the best
46            // this ghost can do without tracking state from the original
47            // `store` call.
48            retention_until: retention_deadline(now, 10),
49        })
50    }
51
52    async fn verify(
53        &self,
54        _passport_id: PassportId,
55        _expected_hash: &str,
56    ) -> Result<BackupVerification, DppError> {
57        Ok(BackupVerification {
58            integrity_ok: false,
59            accessible: false,
60            status: BackupStatus::Expired,
61            last_verified_at: Utc::now(),
62        })
63    }
64
65    async fn retrieve(&self, _passport_id: PassportId) -> Result<Option<Passport>, DppError> {
66        Ok(None)
67    }
68}