1use crate::error::{BpError, BpResult};
7use serde::{Deserialize, Serialize};
8use std::path::Path;
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct PouchMeta {
16 pub network_id: String,
18 pub service_id: String,
20 pub storage_bytes_bid: u64,
22 pub storage_bytes_used: u64,
24 pub joined_at: u64,
26}
27
28impl PouchMeta {
29 pub fn new(network_id: String, service_id: String, storage_bytes_bid: u64) -> Self {
31 Self {
32 network_id,
33 service_id,
34 storage_bytes_bid,
35 storage_bytes_used: 0,
36 joined_at: chrono::Utc::now().timestamp() as u64,
37 }
38 }
39
40 pub fn available_bytes(&self) -> u64 {
42 self.storage_bytes_bid
43 .saturating_sub(self.storage_bytes_used)
44 }
45
46 pub fn has_capacity(&self, bytes: u64) -> bool {
48 self.available_bytes() >= bytes
49 }
50
51 pub fn load(path: &Path) -> BpResult<Self> {
53 let json = std::fs::read_to_string(path).map_err(BpError::Io)?;
54 serde_json::from_str(&json).map_err(BpError::Serde)
55 }
56
57 pub fn save(&self, path: &Path) -> BpResult<()> {
59 let json = serde_json::to_string_pretty(self)?;
60 std::fs::write(path, json).map_err(BpError::Io)
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn available_bytes_correct() {
70 let mut m = PouchMeta::new("net".into(), "svc".into(), 1000);
71 assert_eq!(m.available_bytes(), 1000);
72 m.storage_bytes_used = 400;
73 assert_eq!(m.available_bytes(), 600);
74 }
75
76 #[test]
77 fn has_capacity_boundary() {
78 let mut m = PouchMeta::new("net".into(), "svc".into(), 100);
79 assert!(m.has_capacity(100));
80 assert!(!m.has_capacity(101));
81 m.storage_bytes_used = 50;
82 assert!(m.has_capacity(50));
83 assert!(!m.has_capacity(51));
84 }
85
86 #[test]
87 #[cfg(unix)]
88 fn save_and_load_roundtrip() {
89 let dir =
90 std::env::temp_dir().join(format!("bp_meta_test_{}", uuid::Uuid::new_v4().simple()));
91 std::fs::create_dir_all(&dir).unwrap();
92 let path = dir.join("meta.json");
93
94 let meta = PouchMeta::new("amici".into(), "svc-uuid".into(), 10_737_418_240);
95 meta.save(&path).unwrap();
96
97 let loaded = PouchMeta::load(&path).unwrap();
98 assert_eq!(loaded.network_id, "amici");
99 assert_eq!(loaded.storage_bytes_bid, 10_737_418_240);
100 assert_eq!(loaded.storage_bytes_used, 0);
101
102 std::fs::remove_dir_all(dir).ok();
103 }
104}