hwpforge_core/
metadata.rs1use schemars::JsonSchema;
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
50pub struct Metadata {
51 pub title: Option<String>,
53 pub author: Option<String>,
55 pub subject: Option<String>,
57 pub keywords: Vec<String>,
59 pub created: Option<String>,
61 pub modified: Option<String>,
63}
64
65impl std::fmt::Display for Metadata {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 match &self.title {
68 Some(t) => write!(f, "Metadata(\"{}\")", t),
69 None => write!(f, "Metadata(untitled)"),
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn default_is_all_none_or_empty() {
80 let m = Metadata::default();
81 assert!(m.title.is_none());
82 assert!(m.author.is_none());
83 assert!(m.subject.is_none());
84 assert!(m.keywords.is_empty());
85 assert!(m.created.is_none());
86 assert!(m.modified.is_none());
87 }
88
89 #[test]
90 fn struct_literal_construction() {
91 let m = Metadata {
92 title: Some("Test".to_string()),
93 author: Some("Author".to_string()),
94 subject: Some("Subject".to_string()),
95 keywords: vec!["rust".to_string(), "hwp".to_string()],
96 created: Some("2026-02-07T00:00:00Z".to_string()),
97 modified: Some("2026-02-07T12:00:00Z".to_string()),
98 };
99 assert_eq!(m.title.as_deref(), Some("Test"));
100 assert_eq!(m.keywords.len(), 2);
101 }
102
103 #[test]
104 fn partial_construction_with_defaults() {
105 let m = Metadata { title: Some("Report".to_string()), ..Metadata::default() };
106 assert_eq!(m.title.as_deref(), Some("Report"));
107 assert!(m.author.is_none());
108 }
109
110 #[test]
111 fn display_with_title() {
112 let m = Metadata { title: Some("My Doc".to_string()), ..Metadata::default() };
113 assert_eq!(m.to_string(), "Metadata(\"My Doc\")");
114 }
115
116 #[test]
117 fn display_without_title() {
118 let m = Metadata::default();
119 assert_eq!(m.to_string(), "Metadata(untitled)");
120 }
121
122 #[test]
123 fn equality() {
124 let a = Metadata { title: Some("A".to_string()), ..Metadata::default() };
125 let b = Metadata { title: Some("A".to_string()), ..Metadata::default() };
126 let c = Metadata { title: Some("B".to_string()), ..Metadata::default() };
127 assert_eq!(a, b);
128 assert_ne!(a, c);
129 }
130
131 #[test]
132 fn clone_independence() {
133 let m = Metadata { title: Some("Original".to_string()), ..Metadata::default() };
134 let mut cloned = m.clone();
135 cloned.title = Some("Modified".to_string());
136 assert_eq!(m.title.as_deref(), Some("Original"));
137 }
138
139 #[test]
140 fn korean_text() {
141 let m = Metadata {
142 title: Some("분기 보고서".to_string()),
143 author: Some("김철수".to_string()),
144 keywords: vec!["한글".to_string(), "보고서".to_string()],
145 ..Metadata::default()
146 };
147 assert_eq!(m.title.as_deref(), Some("분기 보고서"));
148 }
149
150 #[test]
151 fn serde_roundtrip() {
152 let m = Metadata {
153 title: Some("Test".to_string()),
154 author: Some("Author".to_string()),
155 subject: None,
156 keywords: vec!["a".to_string(), "b".to_string()],
157 created: Some("2026-02-07T00:00:00Z".to_string()),
158 modified: None,
159 };
160 let json = serde_json::to_string(&m).unwrap();
161 let back: Metadata = serde_json::from_str(&json).unwrap();
162 assert_eq!(m, back);
163 }
164
165 #[test]
166 fn serde_default_roundtrip() {
167 let m = Metadata::default();
168 let json = serde_json::to_string(&m).unwrap();
169 let back: Metadata = serde_json::from_str(&json).unwrap();
170 assert_eq!(m, back);
171 }
172
173 #[test]
174 fn empty_keywords_serializes_as_empty_array() {
175 let m = Metadata::default();
176 let json = serde_json::to_string(&m).unwrap();
177 assert!(json.contains("\"keywords\":[]"), "json: {json}");
178 }
179}