sheetkit_xml/
content_types.rs1use serde::{Deserialize, Serialize};
6
7use crate::namespaces;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11#[serde(rename = "Types")]
12pub struct ContentTypes {
13 #[serde(rename = "@xmlns")]
14 pub xmlns: String,
15
16 #[serde(rename = "Default", default)]
17 pub defaults: Vec<ContentTypeDefault>,
18
19 #[serde(rename = "Override", default)]
20 pub overrides: Vec<ContentTypeOverride>,
21}
22
23#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25pub struct ContentTypeDefault {
26 #[serde(rename = "@Extension")]
27 pub extension: String,
28
29 #[serde(rename = "@ContentType")]
30 pub content_type: String,
31}
32
33#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35pub struct ContentTypeOverride {
36 #[serde(rename = "@PartName")]
37 pub part_name: String,
38
39 #[serde(rename = "@ContentType")]
40 pub content_type: String,
41}
42
43impl Default for ContentTypes {
44 fn default() -> Self {
45 Self {
46 xmlns: namespaces::CONTENT_TYPES.to_string(),
47 defaults: vec![
48 ContentTypeDefault {
49 extension: "rels".to_string(),
50 content_type: mime_types::RELS.to_string(),
51 },
52 ContentTypeDefault {
53 extension: "xml".to_string(),
54 content_type: mime_types::XML.to_string(),
55 },
56 ],
57 overrides: vec![
58 ContentTypeOverride {
59 part_name: "/xl/workbook.xml".to_string(),
60 content_type: mime_types::WORKBOOK.to_string(),
61 },
62 ContentTypeOverride {
63 part_name: "/xl/worksheets/sheet1.xml".to_string(),
64 content_type: mime_types::WORKSHEET.to_string(),
65 },
66 ContentTypeOverride {
67 part_name: "/xl/styles.xml".to_string(),
68 content_type: mime_types::STYLES.to_string(),
69 },
70 ContentTypeOverride {
71 part_name: "/xl/sharedStrings.xml".to_string(),
72 content_type: mime_types::SHARED_STRINGS.to_string(),
73 },
74 ],
75 }
76 }
77}
78
79pub mod mime_types {
81 pub const RELS: &str = "application/vnd.openxmlformats-package.relationships+xml";
83 pub const XML: &str = "application/xml";
84 pub const PNG: &str = "image/png";
85 pub const JPEG: &str = "image/jpeg";
86 pub const GIF: &str = "image/gif";
87 pub const BMP: &str = "image/bmp";
88 pub const ICO: &str = "image/x-icon";
89 pub const TIFF: &str = "image/tiff";
90 pub const SVG: &str = "image/svg+xml";
91 pub const EMF: &str = "image/x-emf";
92 pub const EMZ: &str = "image/x-emz";
93 pub const WMF: &str = "image/x-wmf";
94 pub const WMZ: &str = "image/x-wmz";
95
96 pub const WORKBOOK: &str =
98 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml";
99 pub const WORKBOOK_MACRO: &str = "application/vnd.ms-excel.sheet.macroEnabled.main+xml";
100 pub const WORKBOOK_TEMPLATE: &str =
101 "application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml";
102 pub const WORKBOOK_TEMPLATE_MACRO: &str =
103 "application/vnd.ms-excel.template.macroEnabled.main+xml";
104 pub const WORKBOOK_ADDIN_MACRO: &str = "application/vnd.ms-excel.addin.macroEnabled.main+xml";
105
106 pub const WORKSHEET: &str =
108 "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";
109 pub const CHARTSHEET: &str =
110 "application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml";
111
112 pub const SHARED_STRINGS: &str =
114 "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml";
115 pub const STYLES: &str =
116 "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml";
117 pub const THEME: &str = "application/vnd.openxmlformats-officedocument.theme+xml";
118
119 pub const CHART: &str = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
121 pub const DRAWING: &str = "application/vnd.openxmlformats-officedocument.drawing+xml";
122
123 pub const TABLE: &str = "application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml";
125
126 pub const VML_DRAWING: &str = "application/vnd.openxmlformats-officedocument.vmlDrawing";
128
129 pub const COMMENTS: &str =
131 "application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml";
132
133 pub const PIVOT_TABLE: &str =
135 "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml";
136 pub const PIVOT_CACHE_DEFINITION: &str =
137 "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml";
138 pub const PIVOT_CACHE_RECORDS: &str =
139 "application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml";
140
141 pub const SLICER: &str = "application/vnd.ms-excel.slicer+xml";
143 pub const SLICER_CACHE: &str = "application/vnd.ms-excel.slicerCache+xml";
144
145 pub const CORE_PROPERTIES: &str = "application/vnd.openxmlformats-package.core-properties+xml";
147 pub const EXTENDED_PROPERTIES: &str =
148 "application/vnd.openxmlformats-officedocument.extended-properties+xml";
149 pub const CUSTOM_PROPERTIES: &str =
150 "application/vnd.openxmlformats-officedocument.custom-properties+xml";
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_content_types_default() {
159 let ct = ContentTypes::default();
160 assert_eq!(ct.xmlns, namespaces::CONTENT_TYPES);
161 assert_eq!(ct.defaults.len(), 2);
162 assert_eq!(ct.overrides.len(), 4);
163
164 assert_eq!(ct.defaults[0].extension, "rels");
166 assert_eq!(ct.defaults[1].extension, "xml");
167
168 let part_names: Vec<&str> = ct.overrides.iter().map(|o| o.part_name.as_str()).collect();
170 assert!(part_names.contains(&"/xl/workbook.xml"));
171 assert!(part_names.contains(&"/xl/worksheets/sheet1.xml"));
172 assert!(part_names.contains(&"/xl/styles.xml"));
173 assert!(part_names.contains(&"/xl/sharedStrings.xml"));
174 }
175
176 #[test]
177 fn test_content_types_roundtrip() {
178 let ct = ContentTypes::default();
179 let xml = quick_xml::se::to_string(&ct).unwrap();
180 let parsed: ContentTypes = quick_xml::de::from_str(&xml).unwrap();
181 assert_eq!(ct.defaults.len(), parsed.defaults.len());
182 assert_eq!(ct.overrides.len(), parsed.overrides.len());
183 assert_eq!(ct.xmlns, parsed.xmlns);
184 }
185
186 #[test]
187 fn test_content_types_serialize_structure() {
188 let ct = ContentTypes::default();
189 let xml = quick_xml::se::to_string(&ct).unwrap();
190
191 assert!(xml.contains("<Types"));
193 assert!(xml.contains("xmlns="));
194 assert!(xml.contains("<Default"));
196 assert!(xml.contains("Extension="));
197 assert!(xml.contains("ContentType="));
198 assert!(xml.contains("<Override"));
200 assert!(xml.contains("PartName="));
201 }
202
203 #[test]
204 fn test_parse_real_excel_content_types() {
205 let xml = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
206<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
207 <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
208 <Default Extension="xml" ContentType="application/xml"/>
209 <Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
210 <Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
211 <Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>
212 <Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>
213</Types>"#;
214
215 let parsed: ContentTypes = quick_xml::de::from_str(xml).unwrap();
216 assert_eq!(parsed.defaults.len(), 2);
217 assert_eq!(parsed.overrides.len(), 4);
218 assert_eq!(parsed.defaults[0].extension, "rels");
219 assert_eq!(parsed.overrides[0].part_name, "/xl/workbook.xml");
220 }
221
222 #[test]
223 fn test_content_type_default_fields() {
224 let default = ContentTypeDefault {
225 extension: "png".to_string(),
226 content_type: mime_types::PNG.to_string(),
227 };
228 let xml = quick_xml::se::to_string(&default).unwrap();
229 assert!(xml.contains("Extension=\"png\""));
230 assert!(xml.contains("ContentType=\"image/png\""));
231 }
232}