1use crate::error::Result;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8pub struct CardMetadata {
9 pub id: String,
11
12 pub compressed_size: u64,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub original_size: Option<u64>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub profile: Option<String>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub created: Option<u64>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub dict_version: Option<String>,
30}
31
32impl CardMetadata {
33 pub fn new(id: impl Into<String>, compressed_size: u64) -> Self {
35 Self {
36 id: id.into(),
37 compressed_size,
38 original_size: None,
39 profile: None,
40 created: None,
41 dict_version: None,
42 }
43 }
44
45 pub fn with_original_size(mut self, size: u64) -> Self {
47 self.original_size = Some(size);
48 self
49 }
50
51 pub fn with_profile(mut self, profile: impl Into<String>) -> Self {
53 self.profile = Some(profile.into());
54 self
55 }
56
57 pub fn with_timestamp(mut self, timestamp: u64) -> Self {
59 self.created = Some(timestamp);
60 self
61 }
62
63 pub fn with_dict_version(mut self, version: impl Into<String>) -> Self {
65 self.dict_version = Some(version.into());
66 self
67 }
68
69 pub fn to_json(&self) -> Result<Vec<u8>> {
71 Ok(serde_json::to_vec(self)?)
72 }
73
74 pub fn from_json(bytes: &[u8]) -> Result<Self> {
76 Ok(serde_json::from_slice(bytes)?)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_metadata_new() {
86 let metadata = CardMetadata::new("test::doc", 1234);
87 assert_eq!(metadata.id, "test::doc");
88 assert_eq!(metadata.compressed_size, 1234);
89 assert_eq!(metadata.original_size, None);
90 }
91
92 #[test]
93 fn test_metadata_builder() {
94 let metadata = CardMetadata::new("test::doc", 1234)
95 .with_original_size(5678)
96 .with_profile("code:api")
97 .with_timestamp(1703001234567)
98 .with_dict_version("cml-core-v1");
99
100 assert_eq!(metadata.original_size, Some(5678));
101 assert_eq!(metadata.profile.as_deref(), Some("code:api"));
102 assert_eq!(metadata.created, Some(1703001234567));
103 assert_eq!(metadata.dict_version.as_deref(), Some("cml-core-v1"));
104 }
105
106 #[test]
107 fn test_metadata_json_roundtrip() {
108 let metadata = CardMetadata::new("test::doc", 1234)
109 .with_original_size(5678)
110 .with_profile("code:api")
111 .with_timestamp(1703001234567);
112
113 let json = metadata.to_json().unwrap();
114 let loaded = CardMetadata::from_json(&json).unwrap();
115
116 assert_eq!(loaded, metadata);
117 }
118
119 #[test]
120 fn test_metadata_optional_fields() {
121 let metadata = CardMetadata::new("minimal", 100);
122 let json = metadata.to_json().unwrap();
123 let json_str = String::from_utf8(json).unwrap();
124
125 assert!(!json_str.contains("original_size"));
127 assert!(!json_str.contains("profile"));
128 assert!(!json_str.contains("created"));
129 assert!(!json_str.contains("dict_version"));
130 }
131}