Skip to main content

spn_core/backup/
manifest.rs

1//! Backup manifest types.
2//!
3//! The manifest stores metadata about a backup, including versions,
4//! timestamps, and checksums for integrity verification.
5
6use std::collections::HashMap;
7
8/// Backup manifest containing metadata about the backup.
9#[derive(Debug, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct BackupManifest {
12    /// Manifest format version (e.g., "1.0.0")
13    pub version: String,
14
15    /// When the backup was created (ISO 8601 format)
16    pub created_at: String,
17
18    /// Optional user-provided label
19    pub label: Option<String>,
20
21    /// Machine hostname
22    pub hostname: String,
23
24    /// SuperNovae component versions
25    pub versions: ComponentVersions,
26
27    /// SHA-256 checksums for each backed-up file (relative path -> hex checksum)
28    pub checksums: HashMap<String, String>,
29
30    /// What was included in the backup
31    pub contents: BackupContents,
32}
33
34impl BackupManifest {
35    /// Create a new manifest with default values.
36    pub fn new(hostname: String, spn_version: String) -> Self {
37        Self {
38            version: "1.0.0".to_string(),
39            created_at: String::new(),
40            label: None,
41            hostname,
42            versions: ComponentVersions {
43                novanet: None,
44                nika: None,
45                spn: spn_version,
46            },
47            checksums: HashMap::new(),
48            contents: BackupContents::default(),
49        }
50    }
51}
52
53/// Component version information.
54#[derive(Debug, Clone, Default)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct ComponentVersions {
57    /// NovaNet version if available
58    pub novanet: Option<String>,
59    /// Nika version if available
60    pub nika: Option<String>,
61    /// spn version (always present)
62    pub spn: String,
63}
64
65/// Summary of what each subsystem contributed to the backup.
66#[derive(Debug, Clone, Default)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68pub struct BackupContents {
69    /// NovaNet backup contents
70    pub novanet: NovaNetContents,
71    /// Nika backup contents
72    pub nika: NikaContents,
73    /// spn backup contents
74    pub spn: SpnContents,
75}
76
77/// NovaNet-specific backup contents.
78#[derive(Debug, Clone, Default)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct NovaNetContents {
81    /// Number of schema YAML files backed up
82    pub schema_files: u32,
83    /// Number of seed YAML files backed up
84    pub seed_files: u32,
85    /// Whether a Neo4j dump was included (optional, v2 feature)
86    pub neo4j_dump: bool,
87}
88
89/// Nika-specific backup contents.
90#[derive(Debug, Clone, Default)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
92pub struct NikaContents {
93    /// Number of workflow YAML files backed up
94    pub workflow_files: u32,
95    /// Number of chat sessions backed up
96    pub session_count: u32,
97    /// Number of execution traces backed up
98    pub trace_count: u32,
99}
100
101/// spn-specific backup contents.
102#[derive(Debug, Clone, Default)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
104pub struct SpnContents {
105    /// Whether config.toml was backed up
106    pub has_config: bool,
107    /// Whether mcp.yaml was backed up
108    pub has_mcp_yaml: bool,
109    /// Whether jobs.json was backed up
110    pub has_jobs: bool,
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_manifest_new() {
119        let manifest = BackupManifest::new("test-host".to_string(), "0.15.0".to_string());
120        assert_eq!(manifest.version, "1.0.0");
121        assert_eq!(manifest.hostname, "test-host");
122        assert_eq!(manifest.versions.spn, "0.15.0");
123        assert!(manifest.versions.novanet.is_none());
124    }
125
126    #[test]
127    fn test_backup_contents_default() {
128        let contents = BackupContents::default();
129        assert_eq!(contents.novanet.schema_files, 0);
130        assert!(!contents.spn.has_config);
131    }
132}