1use std::io::Cursor;
8
9use quick_xml::events::Event;
10use quick_xml::{Reader, Writer};
11use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
12use std::fmt;
13
14#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct AttrValString {
21 #[serde(rename = "@val", default)]
22 pub val: Option<String>,
23}
24
25#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct AttrValInt {
28 #[serde(rename = "@val", default)]
29 pub val: Option<i64>,
30}
31
32#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
34pub struct AttrValFloat {
35 #[serde(rename = "@val", default)]
36 pub val: Option<f64>,
37}
38
39#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct AttrValBool {
42 #[serde(
43 rename = "@val",
44 default,
45 serialize_with = "serialize_attr_val_bool",
46 deserialize_with = "deserialize_attr_val_bool"
47 )]
48 pub val: Option<bool>,
49}
50
51impl AttrValBool {
52 pub fn value(&self) -> bool {
54 self.val.unwrap_or(false)
55 }
56}
57
58fn serialize_attr_val_bool<S>(value: &Option<bool>, serializer: S) -> Result<S::Ok, S::Error>
59where
60 S: Serializer,
61{
62 match value {
63 Some(true) => serializer.serialize_str("1"),
64 _ => serializer.serialize_str("0"),
65 }
66}
67
68fn deserialize_attr_val_bool<'de, D>(deserializer: D) -> Result<Option<bool>, D::Error>
69where
70 D: Deserializer<'de>,
71{
72 struct AttrValBoolVisitor;
73
74 impl<'de> Visitor<'de> for AttrValBoolVisitor {
75 type Value = Option<bool>;
76
77 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
78 formatter.write_str("a boolean attribute value (0, 1, true, false, or empty)")
79 }
80
81 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
82 where
83 E: serde::de::Error,
84 {
85 match value {
86 "" | "1" | "true" => Ok(Some(true)),
87 "0" | "false" => Ok(Some(false)),
88 _ => Err(E::custom(format!(
89 "invalid boolean attribute value: {}",
90 value
91 ))),
92 }
93 }
94
95 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
96 where
97 E: serde::de::Error,
98 {
99 Ok(Some(value))
100 }
101 }
102
103 deserializer.deserialize_any(AttrValBoolVisitor)
104}
105
106impl AttrValString {
107 pub fn value(&self) -> String {
109 self.val.clone().unwrap_or_default()
110 }
111}
112
113impl AttrValInt {
114 pub fn value(&self) -> i64 {
116 self.val.unwrap_or(0)
117 }
118}
119
120impl AttrValFloat {
121 pub fn value(&self) -> f64 {
123 self.val.unwrap_or(0.0)
124 }
125}
126
127#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
133pub struct XlsxColor {
134 #[serde(rename = "@auto", default, skip_serializing_if = "Option::is_none")]
135 pub auto: Option<bool>,
136 #[serde(rename = "@indexed", default, skip_serializing_if = "Option::is_none")]
137 pub indexed: Option<i64>,
138 #[serde(rename = "@rgb", default, skip_serializing_if = "Option::is_none")]
139 pub rgb: Option<String>,
140 #[serde(rename = "@theme", default, skip_serializing_if = "Option::is_none")]
141 pub theme: Option<i64>,
142 #[serde(rename = "@tint", default, skip_serializing_if = "Option::is_none")]
143 pub tint: Option<f64>,
144}
145
146#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
152pub struct XlsxT {
153 #[serde(rename = "@xml:space", default)]
154 pub space: Option<String>,
155 #[serde(rename = "$value", default)]
156 pub val: String,
157}
158
159#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
161pub struct XlsxR {
162 #[serde(rename = "rPr", default)]
163 pub r_pr: Option<XlsxRPr>,
164 #[serde(rename = "t", default)]
165 pub t: Option<XlsxT>,
166}
167
168#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
170pub struct XlsxRPr {
171 #[serde(rename = "rFont", default)]
172 pub r_font: Option<AttrValString>,
173 #[serde(rename = "charset", default)]
174 pub charset: Option<AttrValInt>,
175 #[serde(rename = "family", default)]
176 pub family: Option<AttrValInt>,
177 #[serde(rename = "b", default)]
178 pub b: Option<AttrValBool>,
179 #[serde(rename = "i", default)]
180 pub i: Option<AttrValBool>,
181 #[serde(rename = "strike", default)]
182 pub strike: Option<AttrValBool>,
183 #[serde(rename = "outline", default)]
184 pub outline: Option<AttrValBool>,
185 #[serde(rename = "shadow", default)]
186 pub shadow: Option<AttrValBool>,
187 #[serde(rename = "condense", default)]
188 pub condense: Option<AttrValBool>,
189 #[serde(rename = "extend", default)]
190 pub extend: Option<AttrValBool>,
191 #[serde(rename = "color", default)]
192 pub color: Option<XlsxColor>,
193 #[serde(rename = "sz", default)]
194 pub sz: Option<AttrValFloat>,
195 #[serde(rename = "u", default)]
196 pub u: Option<AttrValString>,
197 #[serde(rename = "vertAlign", default)]
198 pub vert_align: Option<AttrValString>,
199 #[serde(rename = "scheme", default)]
200 pub scheme: Option<AttrValString>,
201}
202
203#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
205pub struct XlsxPhoneticRun {
206 #[serde(rename = "@sb", default)]
207 pub sb: u32,
208 #[serde(rename = "@eb", default)]
209 pub eb: u32,
210 #[serde(rename = "t", default)]
211 pub t: String,
212}
213
214#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
216pub struct XlsxPhoneticPr {
217 #[serde(rename = "@fontId", default)]
218 pub font_id: Option<i64>,
219 #[serde(rename = "@type", default)]
220 pub r#type: Option<String>,
221 #[serde(rename = "@alignment", default)]
222 pub alignment: Option<String>,
223}
224
225#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
227pub struct RichTextRun {
228 #[serde(default)]
229 pub font: Option<crate::styles::Font>,
230 #[serde(default)]
231 pub text: String,
232}
233
234#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
240pub struct XlsxExtLst {
241 #[serde(rename = "ext", default)]
242 pub ext: Vec<XlsxExt>,
243}
244
245#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
247pub struct XlsxExt {
248 #[serde(rename = "@uri", default)]
249 pub uri: Option<String>,
250 #[serde(
251 rename = "@xmlns:x14",
252 default,
253 skip_serializing_if = "Option::is_none"
254 )]
255 pub xmlns_x14: Option<String>,
256 #[serde(rename = "@xmlns:xm", default, skip_serializing_if = "Option::is_none")]
257 pub xmlns_xm: Option<String>,
258 #[serde(rename = "$value", default)]
259 pub content: String,
260}
261
262pub fn parse_ext_lst_content(xml: &str) -> Result<XlsxExtLst, quick_xml::Error> {
265 let wrapped = format!("<extLst>{xml}</extLst>");
266 let mut reader = Reader::from_str(&wrapped);
267 reader.config_mut().trim_text(true);
268 let mut buf = Vec::new();
269 let mut ext_lst = XlsxExtLst::default();
270 let mut current: Option<XlsxExt> = None;
271 let mut depth = 0;
272 let mut content_buf: Vec<u8> = Vec::new();
273 let mut writer: Option<Writer<Cursor<&mut Vec<u8>>>> = None;
274
275 loop {
276 match reader.read_event_into(&mut buf) {
277 Ok(Event::Start(e)) => {
278 if e.name().as_ref() == b"ext" && depth == 0 {
279 let mut ext = XlsxExt::default();
280 for attr in e.attributes() {
281 let attr = attr?;
282 let value = String::from_utf8_lossy(&attr.value).to_string();
283 match attr.key.as_ref() {
284 b"uri" => ext.uri = Some(value),
285 b"xmlns:x14" => ext.xmlns_x14 = Some(value),
286 b"xmlns:xm" => ext.xmlns_xm = Some(value),
287 _ => {}
288 }
289 }
290 current = Some(ext);
291 depth = 1;
292 content_buf.clear();
293 writer = Some(Writer::new(Cursor::new(&mut content_buf)));
294 } else if depth > 0 {
295 depth += 1;
296 if let Some(ref mut w) = writer {
297 w.write_event(Event::Start(e))?;
298 }
299 }
300 }
301 Ok(Event::End(e)) => {
302 if e.name().as_ref() == b"ext" && depth == 1 {
303 if let Some(mut ext) = current.take() {
304 writer = None;
305 ext.content = String::from_utf8_lossy(&content_buf).to_string();
306 ext_lst.ext.push(ext);
307 }
308 depth = 0;
309 } else if depth > 0 {
310 depth -= 1;
311 if let Some(ref mut w) = writer {
312 w.write_event(Event::End(e))?;
313 }
314 }
315 }
316 Ok(Event::Empty(e)) => {
317 if depth > 0 {
318 if let Some(ref mut w) = writer {
319 w.write_event(Event::Empty(e))?;
320 }
321 }
322 }
323 Ok(Event::Text(e)) => {
324 if depth > 0 {
325 if let Some(ref mut w) = writer {
326 w.write_event(Event::Text(e))?;
327 }
328 }
329 }
330 Ok(Event::CData(e)) => {
331 if depth > 0 {
332 if let Some(ref mut w) = writer {
333 w.write_event(Event::CData(e))?;
334 }
335 }
336 }
337 Ok(Event::Comment(e)) => {
338 if depth > 0 {
339 if let Some(ref mut w) = writer {
340 w.write_event(Event::Comment(e))?;
341 }
342 }
343 }
344 Ok(Event::PI(e)) => {
345 if depth > 0 {
346 if let Some(ref mut w) = writer {
347 w.write_event(Event::PI(e))?;
348 }
349 }
350 }
351 Ok(Event::DocType(e)) => {
352 if depth > 0 {
353 if let Some(ref mut w) = writer {
354 w.write_event(Event::DocType(e))?;
355 }
356 }
357 }
358 Ok(Event::Decl(_)) => {}
359 Ok(Event::Eof) => break,
360 Err(e) => return Err(e),
361 }
362 buf.clear();
363 }
364 Ok(ext_lst)
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
370
371 #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
372 struct TestBool {
373 #[serde(rename = "b", default)]
374 b: Option<AttrValBool>,
375 }
376
377 #[test]
378 fn test_attr_val_bool_serialization() {
379 let t = TestBool {
380 b: Some(AttrValBool { val: Some(true) }),
381 };
382 let xml = quick_xml::se::to_string(&t).unwrap();
383 assert!(xml.contains(r#"<b val="1"/>"#), "{}", xml);
384
385 let f = TestBool {
386 b: Some(AttrValBool { val: Some(false) }),
387 };
388 let xml = quick_xml::se::to_string(&f).unwrap();
389 assert!(xml.contains(r#"<b val="0"/>"#), "{}", xml);
390 }
391
392 #[test]
393 fn test_attr_val_bool_deserialization() {
394 let xml = r#"<TestBool><b val="1"/></TestBool>"#;
395 let t: TestBool = quick_xml::de::from_str(xml).unwrap();
396 assert_eq!(t.b.unwrap().val, Some(true));
397
398 let xml = r#"<TestBool><b val="0"/></TestBool>"#;
399 let t: TestBool = quick_xml::de::from_str(xml).unwrap();
400 assert_eq!(t.b.unwrap().val, Some(false));
401
402 let xml = r#"<TestBool/>"#;
403 let t: TestBool = quick_xml::de::from_str(xml).unwrap();
404 assert_eq!(t.b, None);
405 }
406}
407
408pub fn serialize_ext_lst(ext_lst: &XlsxExtLst) -> String {
411 let mut s = String::new();
412 for ext in &ext_lst.ext {
413 s.push_str("<ext");
414 if let Some(uri) = &ext.uri {
415 s.push_str(&format!(r#" uri="{}""#, uri));
416 }
417 if let Some(ns) = &ext.xmlns_x14 {
418 s.push_str(&format!(r#" xmlns:x14="{}""#, ns));
419 }
420 if let Some(ns) = &ext.xmlns_xm {
421 s.push_str(&format!(r#" xmlns:xm="{}""#, ns));
422 }
423 s.push('>');
424 s.push_str(&ext.content);
425 s.push_str("</ext>");
426 }
427 s
428}
429
430#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
436pub struct XlsxInnerXml {
437 #[serde(rename = "$value", default)]
438 pub content: String,
439}