easyofd_core/doc/
ct_doc_info.rs1use std::fmt::Write;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum DocUsage {
12 Normal,
14 EBook,
16 ENewsPaper,
18 EMagazine,
20}
21
22impl DocUsage {
23 #[must_use]
25 pub fn as_str(&self) -> &'static str {
26 match self {
27 Self::Normal => "Normal",
28 Self::EBook => "EBook",
29 Self::ENewsPaper => "ENewsPaper",
30 Self::EMagazine => "EMagazine",
31 }
32 }
33
34 #[allow(clippy::should_implement_trait)]
36 pub fn from_str(s: &str) -> Result<Self, String> {
37 match s {
38 "Normal" => Ok(Self::Normal),
39 "EBook" => Ok(Self::EBook),
40 "ENewsPaper" => Ok(Self::ENewsPaper),
41 "EMagazine" => Ok(Self::EMagazine),
42 _ => Err(format!("未知的文档用途: {s}")),
43 }
44 }
45}
46
47impl std::fmt::Display for DocUsage {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.write_str(self.as_str())
50 }
51}
52
53#[derive(Debug, Clone)]
57pub struct CtDocInfo {
58 pub doc_id: String,
60 pub title: Option<String>,
62 pub author: Option<String>,
64 pub subject: Option<String>,
66 pub abstract_text: Option<String>,
68 pub creation_date: Option<String>,
70 pub mod_date: Option<String>,
72 pub doc_usage: Option<DocUsage>,
74 pub cover: Option<String>,
76 pub keywords: Vec<String>,
78 pub creator: Option<String>,
80 pub creator_version: Option<String>,
82}
83
84impl CtDocInfo {
85 #[must_use]
87 pub fn new(doc_id: impl Into<String>) -> Self {
88 Self {
89 doc_id: doc_id.into(),
90 title: None,
91 author: None,
92 subject: None,
93 abstract_text: None,
94 creation_date: None,
95 mod_date: None,
96 doc_usage: None,
97 cover: None,
98 keywords: Vec::new(),
99 creator: None,
100 creator_version: None,
101 }
102 }
103
104 #[must_use]
106 pub fn title(mut self, title: impl Into<String>) -> Self {
107 self.title = Some(title.into());
108 self
109 }
110
111 #[must_use]
113 pub fn author(mut self, author: impl Into<String>) -> Self {
114 self.author = Some(author.into());
115 self
116 }
117
118 #[must_use]
120 pub fn subject(mut self, subject: impl Into<String>) -> Self {
121 self.subject = Some(subject.into());
122 self
123 }
124
125 #[must_use]
127 pub fn abstract_text(mut self, text: impl Into<String>) -> Self {
128 self.abstract_text = Some(text.into());
129 self
130 }
131
132 #[must_use]
134 pub fn creation_date(mut self, date: impl Into<String>) -> Self {
135 self.creation_date = Some(date.into());
136 self
137 }
138
139 #[must_use]
141 pub fn mod_date(mut self, date: impl Into<String>) -> Self {
142 self.mod_date = Some(date.into());
143 self
144 }
145
146 #[must_use]
148 pub fn doc_usage(mut self, usage: DocUsage) -> Self {
149 self.doc_usage = Some(usage);
150 self
151 }
152
153 #[must_use]
155 pub fn cover(mut self, cover: impl Into<String>) -> Self {
156 self.cover = Some(cover.into());
157 self
158 }
159
160 #[must_use]
162 pub fn add_keyword(mut self, keyword: impl Into<String>) -> Self {
163 self.keywords.push(keyword.into());
164 self
165 }
166
167 #[must_use]
169 pub fn creator(mut self, creator: impl Into<String>) -> Self {
170 self.creator = Some(creator.into());
171 self
172 }
173
174 #[must_use]
176 pub fn creator_version(mut self, version: impl Into<String>) -> Self {
177 self.creator_version = Some(version.into());
178 self
179 }
180
181 #[must_use]
183 pub fn to_xml_string(&self) -> String {
184 let mut xml = String::from("<ofd:DocInfo>");
185
186 let _ = write!(xml, "\n<ofd:DocID>{}</ofd:DocID>", self.doc_id);
187
188 if let Some(ref title) = self.title {
189 let _ = write!(xml, "\n<ofd:Title>{title}</ofd:Title>");
190 }
191 if let Some(ref author) = self.author {
192 let _ = write!(xml, "\n<ofd:Author>{author}</ofd:Author>");
193 }
194 if let Some(ref subject) = self.subject {
195 let _ = write!(xml, "\n<ofd:Subject>{subject}</ofd:Subject>");
196 }
197 if let Some(ref abs) = self.abstract_text {
198 let _ = write!(xml, "\n<ofd:Abstract>{abs}</ofd:Abstract>");
199 }
200 if let Some(ref date) = self.creation_date {
201 let _ = write!(xml, "\n<ofd:CreationDate>{date}</ofd:CreationDate>");
202 }
203 if let Some(ref date) = self.mod_date {
204 let _ = write!(xml, "\n<ofd:ModDate>{date}</ofd:ModDate>");
205 }
206 if let Some(ref usage) = self.doc_usage {
207 let _ = write!(xml, "\n<ofd:DocUsage>{}</ofd:DocUsage>", usage.as_str());
208 }
209 if let Some(ref cover) = self.cover {
210 let _ = write!(xml, "\n<ofd:Cover>{cover}</ofd:Cover>");
211 }
212 if !self.keywords.is_empty() {
213 xml.push_str("\n<ofd:Keywords>");
214 for kw in &self.keywords {
215 let _ = write!(xml, "\n<ofd:Keyword>{kw}</ofd:Keyword>");
216 }
217 xml.push_str("\n</ofd:Keywords>");
218 }
219 if let Some(ref c) = self.creator {
220 let _ = write!(xml, "\n<ofd:Creator>{c}</ofd:Creator>");
221 }
222 if let Some(ref v) = self.creator_version {
223 let _ = write!(xml, "\n<ofd:CreatorVersion>{v}</ofd:CreatorVersion>");
224 }
225
226 xml.push_str("\n</ofd:DocInfo>");
227 xml
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_ct_doc_info_new() {
237 let info = CtDocInfo::new("550e8400e29b41d4a716446655440000");
238 assert_eq!(info.doc_id, "550e8400e29b41d4a716446655440000");
239 assert!(info.title.is_none());
240 assert!(info.author.is_none());
241 assert!(info.keywords.is_empty());
242 }
243
244 #[test]
245 fn test_ct_doc_info_builder() {
246 let info = CtDocInfo::new("abc123")
247 .title("测试文档")
248 .author("张三")
249 .subject("测试主题")
250 .creation_date("2025-01-01")
251 .mod_date("2025-06-01")
252 .doc_usage(DocUsage::EBook)
253 .add_keyword("OFD")
254 .add_keyword("测试")
255 .creator("easyofd-rust")
256 .creator_version("1.0");
257 assert_eq!(info.title.as_deref(), Some("测试文档"));
258 assert_eq!(info.author.as_deref(), Some("张三"));
259 assert_eq!(info.doc_usage, Some(DocUsage::EBook));
260 assert_eq!(info.keywords.len(), 2);
261 }
262
263 #[test]
264 fn test_ct_doc_info_xml() {
265 let info = CtDocInfo::new("doc001").title("My Doc").author("Author1");
266 let xml = info.to_xml_string();
267 assert!(xml.contains("<ofd:DocID>doc001</ofd:DocID>"));
268 assert!(xml.contains("<ofd:Title>My Doc</ofd:Title>"));
269 assert!(xml.contains("<ofd:Author>Author1</ofd:Author>"));
270 assert!(xml.contains("</ofd:DocInfo>"));
271 }
272
273 #[test]
274 fn test_doc_usage_display() {
275 assert_eq!(DocUsage::Normal.to_string(), "Normal");
276 assert_eq!(DocUsage::EBook.to_string(), "EBook");
277 assert_eq!(DocUsage::from_str("Normal").unwrap(), DocUsage::Normal);
278 assert!(DocUsage::from_str("Invalid").is_err());
279 }
280}