Skip to main content

shard_core/
index.rs

1use crate::manifest::FileManifest;
2use crate::metadata::{self, MetadataFormat};
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::fs;
7use std::path::Path;
8
9#[derive(Serialize, Deserialize, Default, Debug)]
10pub struct Index {
11    pub files: HashMap<String, FileManifest>,
12}
13
14impl Index {
15    pub fn load(path: &Path, _fmt: &MetadataFormat) -> Result<Self> {
16        if !path.exists() {
17            return Ok(Self::default());
18        }
19        let content = fs::read(path)?;
20        metadata::deserialize(&content)
21    }
22
23    pub fn save(&self, path: &Path, fmt: &MetadataFormat) -> Result<()> {
24        let content = metadata::serialize(self, fmt);
25        fs::write(path, content)?;
26        Ok(())
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use tempfile::tempdir;
34
35    fn make_manifest(name: &str) -> FileManifest {
36        FileManifest {
37            name: name.to_string(),
38            size: 100,
39            chunks: vec!["hash1".into()],
40            content_type: None,
41            compression: "none".into(),
42            merkle_root: None,
43            created_by: None,
44            created_at: None,
45            signature: None,
46        }
47    }
48
49    #[test]
50    fn test_index_load_nonexistent() {
51        let dir = tempdir().unwrap();
52        let idx = Index::load(&dir.path().join("index"), &MetadataFormat::Json).unwrap();
53        assert!(idx.files.is_empty());
54    }
55
56    #[test]
57    fn test_index_save_load_json_roundtrip() {
58        let dir = tempdir().unwrap();
59        let path = dir.path().join("index");
60
61        let mut idx = Index::default();
62        idx.files.insert("a.txt".into(), make_manifest("a.txt"));
63        idx.files.insert("b.txt".into(), make_manifest("b.txt"));
64        idx.save(&path, &MetadataFormat::Json).unwrap();
65
66        let loaded = Index::load(&path, &MetadataFormat::Json).unwrap();
67        assert_eq!(loaded.files.len(), 2);
68        assert!(loaded.files.contains_key("a.txt"));
69        assert!(loaded.files.contains_key("b.txt"));
70    }
71
72    #[test]
73    fn test_index_save_load_cbor_roundtrip() {
74        let dir = tempdir().unwrap();
75        let path = dir.path().join("index");
76
77        let mut idx = Index::default();
78        idx.files.insert("c.txt".into(), make_manifest("c.txt"));
79        idx.save(&path, &MetadataFormat::Cbor).unwrap();
80
81        let loaded = Index::load(&path, &MetadataFormat::Cbor).unwrap();
82        assert_eq!(loaded.files.len(), 1);
83        assert!(loaded.files.contains_key("c.txt"));
84
85        // CBOR file should auto-detect format
86        let loaded_auto = Index::load(&path, &MetadataFormat::Json).unwrap();
87        assert_eq!(loaded_auto.files.len(), 1);
88    }
89
90    #[test]
91    fn test_index_overwrite() {
92        let dir = tempdir().unwrap();
93        let path = dir.path().join("index");
94
95        let mut idx = Index::default();
96        idx.files.insert("x.txt".into(), make_manifest("x.txt"));
97        idx.save(&path, &MetadataFormat::Json).unwrap();
98
99        let mut idx2 = Index::default();
100        idx2.files.insert("y.txt".into(), make_manifest("y.txt"));
101        idx2.save(&path, &MetadataFormat::Json).unwrap();
102
103        let loaded = Index::load(&path, &MetadataFormat::Json).unwrap();
104        assert_eq!(loaded.files.len(), 1);
105        assert!(loaded.files.contains_key("y.txt"));
106    }
107}