1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use std::{borrow::Cow, io::Write};

use super::element::*;
use super::namespace::Namespaces;
use super::variant::*;

use quick_xml::events::attributes::Attribute;
use serde::{Deserialize, Serialize};

pub const APP_PROPERTIES_URI: &str = "docProps/app.xml";

pub const APP_PROPERTIES_TAG: &str = "Properties";
pub const APP_PROPERTIES_NAMESPACE_ATTRIBUTE: &str = "xmlns";
pub const APP_PROPERTIES_NAMESPACE: &str =
    "http://schemas.openxmlformats.org/officeDocument/2006/app-properties";

pub const APP_PROPERTY_TAG: &str = "property";

pub const VT_NAMESPACE_ATTRIBUTE: &str = "xmlns:vt";
pub const VT_NAMESPACE: &str =
    "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes";

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Application(String);

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TitlesOfParts {
    #[serde(rename(deserialize = "$value", serialize = "vt:vector"))]
    value: Variant,
}

#[test]
fn serde_titles_of_parts() {
    let v = Variant::VtVector {
        size: 2,
        base_type: "lpstr".into(),
        variants: vec![
            Variant::VtLpstr("Sheet1".into()),
            Variant::VtLpstr("Sheet2".into()),
        ],
    };
    let xml = quick_xml::se::to_string(&v).unwrap();
    assert_eq!(
        xml,
        r#"<vt:vector size="2" baseType="lpstr"><vt:lpstr>Sheet1</vt:lpstr><vt:lpstr>Sheet2</vt:lpstr></vt:vector>"#
    );

    let v = TitlesOfParts { value: v };
    let xml = quick_xml::se::to_string(&v).unwrap();
    assert_eq!(
        xml,
        r#"<TitlesOfParts><vt:vector size="2" baseType="lpstr"><vt:lpstr>Sheet1</vt:lpstr><vt:lpstr>Sheet2</vt:lpstr></vt:vector></TitlesOfParts>"#
    );

    #[derive(Debug, Serialize, Deserialize, PartialEq)]
    #[serde(rename = "Properties", rename_all = "PascalCase")]
    struct A {
        titles_of_parts: TitlesOfParts,
    }
    let v = A { titles_of_parts: v };
    let xml = quick_xml::se::to_string(&v).unwrap();
    assert_eq!(
        xml,
        r#"<Properties><TitlesOfParts><vt:vector size="2" baseType="lpstr"><vt:lpstr>Sheet1</vt:lpstr><vt:lpstr>Sheet2</vt:lpstr></vt:vector></TitlesOfParts></Properties>"#
    );
    let v2: A = quick_xml::de::from_str(&xml).unwrap();
    assert_eq!(v, v2);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HeadingPairs {
    #[serde(rename = "$value")]
    variant: Variant,
}
/// App properties
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename = "Properties", rename_all = "PascalCase")]
pub struct AppProperties {
    #[serde(flatten, skip_serializing)]
    pub namespaces: Namespaces,
    pub application: Option<Application>,
    pub heading_pairs: Option<HeadingPairs>,
    pub titles_of_parts: Option<TitlesOfParts>,
    pub lines: Option<String>,
    pub links_up_to_date: Option<String>,
    pub local_name: Option<String>,
    pub company: Option<String>,
    pub template: Option<String>,
    pub manager: Option<String>,
    pub pages: Option<String>,
}

impl OpenXmlElementInfo for AppProperties {
    fn tag_name() -> &'static str {
        APP_PROPERTIES_TAG
    }

    fn element_type() -> OpenXmlElementType {
        OpenXmlElementType::Root
    }
}

impl OpenXmlSerialize for AppProperties {
    fn namespaces(&self) -> Option<Cow<Namespaces>> {
        Some(Cow::Borrowed(&self.namespaces))
    }
    fn attributes(&self) -> Option<Vec<Attribute>> {
        None
    }
    fn write_inner<W: Write>(&self, writer: W) -> crate::error::Result<()> {
        let mut writer = quick_xml::Writer::new(writer);

        macro_rules! se_field {
            ($field:ident) => {
                if let Some($field) = &self.$field {
                    quick_xml::se::to_writer(writer.inner(), $field)?;
                }
            };
        }
        se_field!(application);
        se_field!(heading_pairs);
        se_field!(titles_of_parts);

        macro_rules! string_field {
            ($field:ident, $tag:literal) => {
                paste::paste! {
                    if let Some($field) = &self.$field {
                        use quick_xml::events::*;
                        let start = BytesStart::borrowed_name($tag);
                        writer.write_event(Event::Start(start))?;
                        writer.write_event(Event::Text(BytesText::from_plain_str($field)))?;
                        let end = BytesEnd::borrowed($tag);
                        writer.write_event(Event::End(end))?;
                    }
                }
            };
        }
        // string_filed!(links_up_to_date, b"LinksUpToDate");
        string_field!(company, b"Company");
        string_field!(template, b"Template");
        string_field!(manager, b"Manager");
        string_field!(pages, b"Pages");

        Ok(())
    }
}
impl OpenXmlDeserializeDefault for AppProperties {}

#[test]
fn serde() {
    let raw = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>WPS 表格</Application><HeadingPairs><vt:vector size="2" baseType="variant"><vt:variant><vt:lpstr>工作表</vt:lpstr></vt:variant><vt:variant><vt:i4>2</vt:i4></vt:variant></vt:vector></HeadingPairs><TitlesOfParts><vt:vector size="2" baseType="lpstr"><vt:lpstr>Sheet1</vt:lpstr><vt:lpstr>Sheet2</vt:lpstr></vt:vector></TitlesOfParts></Properties>"#;
    let v: AppProperties = AppProperties::from_xml_str(raw).unwrap();
    println!("{:?}", v);
    // let xml = quick_xml::se::to_string(&v).unwrap();
    let xml = v.to_xml_string().unwrap();
    assert_eq!(raw, xml);
}