winbrew_models\catalog/
metadata.rs1use std::collections::BTreeMap;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6use serde::{Deserialize, Serialize};
7
8use crate::shared::ModelError;
9
10pub const SCHEMA_VERSION: u32 = 1;
11pub const CATALOG_DB_SCHEMA_VERSION: u32 = 2;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CatalogMetadata {
17 pub schema_version: u32,
19 pub generated_at_unix: u64,
21 pub current_hash: String,
23 pub previous_hash: String,
25 pub package_count: usize,
27 pub source_counts: BTreeMap<String, usize>,
29}
30
31impl CatalogMetadata {
32 pub fn build_from_counts(
34 package_count: usize,
35 source_counts: BTreeMap<String, usize>,
36 current_hash: String,
37 ) -> Self {
38 Self {
39 schema_version: SCHEMA_VERSION,
40 generated_at_unix: SystemTime::now()
41 .duration_since(UNIX_EPOCH)
42 .unwrap_or_default()
43 .as_secs(),
44 current_hash,
45 previous_hash: String::default(),
46 package_count,
47 source_counts,
48 }
49 }
50
51 pub fn validate(&self) -> Result<(), ModelError> {
53 if self.schema_version != SCHEMA_VERSION {
54 return Err(ModelError::invalid_contract(
55 "catalog_metadata.schema_version",
56 format!(
57 "unsupported catalog metadata schema version: expected {SCHEMA_VERSION}, got {}",
58 self.schema_version
59 ),
60 ));
61 }
62
63 if self.current_hash.trim().is_empty() {
64 return Err(ModelError::invalid_contract(
65 "catalog_metadata.current_hash",
66 "current_hash cannot be empty",
67 ));
68 }
69
70 Ok(())
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::CatalogMetadata;
77 use std::collections::BTreeMap;
78
79 #[test]
80 fn builds_metadata_with_schema_version() {
81 let metadata = CatalogMetadata::build_from_counts(
82 2,
83 BTreeMap::from([(String::from("scoop"), 1)]),
84 String::from("sha256:abc"),
85 );
86
87 assert_eq!(metadata.schema_version, 1);
88 assert_eq!(metadata.package_count, 2);
89 assert_eq!(metadata.source_counts.get("scoop"), Some(&1));
90 }
91}