diff --git a/docx-core/src/documents/build_xml.rs b/docx-core/src/documents/build_xml.rs new file mode 100644 index 0000000..52eb2f9 --- /dev/null +++ b/docx-core/src/documents/build_xml.rs @@ -0,0 +1,3 @@ +pub trait BuildXML { + fn build(&self) -> Vec; +} diff --git a/docx-core/src/documents/content_types.rs b/docx-core/src/documents/content_types.rs index 0a296f3..6770c9f 100644 --- a/docx-core/src/documents/content_types.rs +++ b/docx-core/src/documents/content_types.rs @@ -1,3 +1,4 @@ +use crate::documents::BuildXML; use crate::xml_builder::*; pub struct ContentTypes {} @@ -6,8 +7,10 @@ impl ContentTypes { pub fn new() -> ContentTypes { ContentTypes {} } +} - pub fn build(&self) -> Vec { +impl BuildXML for ContentTypes { + fn build(&self) -> Vec { let b = XMLBuilder::new(); b.declaration(None) .open_types("http://schemas.openxmlformats.org/package/2006/content-types") diff --git a/docx-core/src/documents/doc_props/app.rs b/docx-core/src/documents/doc_props/app.rs index 9ebeb33..c127b7e 100644 --- a/docx-core/src/documents/doc_props/app.rs +++ b/docx-core/src/documents/doc_props/app.rs @@ -1,3 +1,4 @@ +use crate::documents::BuildXML; use crate::xml_builder::*; pub struct AppProps { @@ -19,8 +20,10 @@ impl AppProps { pub fn new(config: Option) -> AppProps { AppProps { config } } +} - pub(crate) fn build(&self) -> Vec { +impl BuildXML for AppProps { + fn build(&self) -> Vec { let b = XMLBuilder::new(); let base = b.declaration(Some(true)).open_properties( "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties", diff --git a/docx-core/src/documents/doc_props/core.rs b/docx-core/src/documents/doc_props/core.rs new file mode 100644 index 0000000..948fbbb --- /dev/null +++ b/docx-core/src/documents/doc_props/core.rs @@ -0,0 +1,131 @@ +use crate::documents::BuildXML; +use crate::xml_builder::*; + +pub struct CoreProps { + config: Option, +} + +pub struct CorePropsConfig { + created: Option, + creator: Option, + description: Option, + language: Option, + last_modified_by: Option, + modified: Option, + revision: Option, + subject: Option, + title: Option, +} + +impl CoreProps { + pub(crate) fn new(config: Option) -> CoreProps { + CoreProps { config } + } +} + +impl BuildXML for CoreProps { + fn build(&self) -> Vec { + let b = XMLBuilder::new(); + let base = b.declaration(Some(true)).open_core_properties( + "http://schemas.openxmlformats.org/package/2006/metadata/core-properties", + "http://purl.org/dc/elements/1.1/", + "http://purl.org/dc/terms/", + "http://purl.org/dc/dcmitype/", + "http://www.w3.org/2001/XMLSchema-instance", + ); + + let convert = |v: usize| format!("{}", v); + let default = || ""; + + if let Some(c) = &self.config { + base.dcterms_created( + "dcterms:W3CDTF", + c.created.as_ref().map_or_else(default, |v| v), + ) + .dc_creator(c.creator.as_ref().map_or_else(default, |v| v)) + .dc_description(c.description.as_ref().map_or_else(default, |v| v)) + .dc_language(c.language.as_ref().map_or_else(default, |v| v)) + .cp_last_modified_by(c.last_modified_by.as_ref().map_or_else(default, |v| v)) + .dcterms_modified( + "dcterms:W3CDTF", + c.modified.as_ref().map_or_else(default, |v| v), + ) + .cp_revision(&c.revision.map_or_else(|| "".to_owned(), convert)) + .dc_subject(c.subject.as_ref().map_or_else(default, |v| v)) + .dc_title(c.title.as_ref().map_or_else(default, |v| v)) + .close() + .build() + } else { + base.dcterms_created("dcterms:W3CDTF", "") + .dc_creator("") + .dc_description("") + .dc_language("") + .cp_last_modified_by("") + .dcterms_modified("dcterms:W3CDTF", "") + .cp_revision("") + .dc_subject("") + .dc_title("") + .close() + .build() + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use std::str; + + #[test] + fn test_default_doc_props_core_build() { + let c = CoreProps::new(None); + let b = c.build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + + + + + + + + + +"# + ); + } + + #[test] + fn test_configured_doc_props_core_build() { + let c = CoreProps::new(Some(CorePropsConfig { + created: Some("2019-01-01".to_owned()), + creator: Some("foo".to_owned()), + description: Some("bar".to_owned()), + language: Some("en".to_owned()), + last_modified_by: Some("go".to_owned()), + modified: Some("2019-01-01".to_owned()), + revision: Some(1), + subject: Some("subject".to_owned()), + title: Some("title".to_owned()), + })); + let b = c.build(); + assert_eq!( + str::from_utf8(&b).unwrap(), + r#" + + 2019-01-01 + foo + bar + en + go + 2019-01-01 + 1 + subject + title +"# + ); + } +} diff --git a/docx-core/src/documents/doc_props/mod.rs b/docx-core/src/documents/doc_props/mod.rs index 3b2c4b5..ba08848 100644 --- a/docx-core/src/documents/doc_props/mod.rs +++ b/docx-core/src/documents/doc_props/mod.rs @@ -1,14 +1,21 @@ mod app; +mod core; -pub use app::*; +pub use self::app::*; +pub use self::core::*; pub(crate) struct DocProps { app: AppProps, + core: CoreProps, } impl DocProps { - pub(crate) fn new(appConfig: Option) -> DocProps { - let app = AppProps::new(appConfig); - DocProps { app } + pub(crate) fn new( + app_config: Option, + core_config: Option, + ) -> DocProps { + let app = AppProps::new(app_config); + let core = CoreProps::new(core_config); + DocProps { app, core } } } diff --git a/docx-core/src/documents/mod.rs b/docx-core/src/documents/mod.rs index e3c490f..54c0be9 100644 --- a/docx-core/src/documents/mod.rs +++ b/docx-core/src/documents/mod.rs @@ -1,8 +1,10 @@ +mod build_xml; mod content_types; mod doc_props; mod rels; mod xml_document; +use build_xml::*; use content_types::*; use doc_props::*; use rels::*; @@ -17,7 +19,7 @@ impl Document { pub fn new() -> Document { let content_type = ContentTypes::new(); let rels = Rels::new(); - let doc_props = DocProps::new(None /* TODO: */); + let doc_props = DocProps::new(None, None /* TODO: */); Document { content_type, rels, diff --git a/docx-core/src/documents/rels.rs b/docx-core/src/documents/rels.rs index 5fa5f0e..01508bf 100644 --- a/docx-core/src/documents/rels.rs +++ b/docx-core/src/documents/rels.rs @@ -1,3 +1,4 @@ +use crate::documents::BuildXML; use crate::xml_builder::*; pub struct Rels {} @@ -6,8 +7,10 @@ impl Rels { pub fn new() -> Rels { Rels {} } +} - pub fn build(&self) -> Vec { +impl BuildXML for Rels { + fn build(&self) -> Vec { let b = XMLBuilder::new(); b.declaration(None) .open_relationships("http://schemas.openxmlformats.org/package/2006/relationships") diff --git a/docx-core/src/documents/xml_document.rs b/docx-core/src/documents/xml_document.rs index bd0ea69..97f129c 100644 --- a/docx-core/src/documents/xml_document.rs +++ b/docx-core/src/documents/xml_document.rs @@ -1,4 +1,5 @@ use super::Document; +use crate::documents::BuildXML; pub(crate) struct XMLDocument { content_type: Vec, diff --git a/docx-core/src/xml_builder/core_properties.rs b/docx-core/src/xml_builder/core_properties.rs new file mode 100644 index 0000000..0504d69 --- /dev/null +++ b/docx-core/src/xml_builder/core_properties.rs @@ -0,0 +1,23 @@ +use super::XMLBuilder; + +impl XMLBuilder { + // i.e. + opened_el!( + open_core_properties, + "cp:coreProperties", + "xmlns:cp", + "xmlns:dc", + "xmlns:dcterms", + "xmlns:dcmitype", + "xmlns:xsi" + ); + closed_el_with_child!(dcterms_created, "dcterms:created", "xsi:type"); + closed_el_with_child!(dc_creator, "dc:creator"); + closed_el_with_child!(dc_description, "dc:description"); + closed_el_with_child!(dc_language, "dc:language"); + closed_el_with_child!(cp_last_modified_by, "cp:lastModifiedBy"); + closed_el_with_child!(dcterms_modified, "dcterms:modified", "xsi:type"); + closed_el_with_child!(cp_revision, "cp:revision"); + closed_el_with_child!(dc_subject, "dc:subject"); + closed_el_with_child!(dc_title, "dc:title"); +} diff --git a/docx-core/src/xml_builder/macros.rs b/docx-core/src/xml_builder/macros.rs index 7c4e56d..6b0c710 100644 --- a/docx-core/src/xml_builder/macros.rs +++ b/docx-core/src/xml_builder/macros.rs @@ -15,6 +15,30 @@ macro_rules! opened_el { self } }; + ($name: ident, $el_name: expr, $attr0: expr, $attr1: expr, $attr2: expr) => { + pub(crate) fn $name(mut self, arg0: &str, arg1: &str, arg2: &str) -> Self { + self.writer + .write(super::XmlEvent::start_element($el_name).attr($attr0, arg0).attr($attr1, arg1).attr($attr2, arg2)) + .expect("should write to buf"); + self + } + }; + ($name: ident, $el_name: expr, $attr0: expr, $attr1: expr, $attr2: expr, $attr3: expr) => { + pub(crate) fn $name(mut self, arg0: &str, arg1: &str, arg2: &str, arg3: &str) -> Self { + self.writer + .write(super::XmlEvent::start_element($el_name).attr($attr0, arg0).attr($attr1, arg1).attr($attr2, arg2).attr($attr3, arg3)) + .expect("should write to buf"); + self + } + }; + ($name: ident, $el_name: expr, $attr0: expr, $attr1: expr, $attr2: expr, $attr3: expr, $attr4: expr) => { + pub(crate) fn $name(mut self, arg0: &str, arg1: &str, arg2: &str, arg3: &str, arg4: &str) -> Self { + self.writer + .write(super::XmlEvent::start_element($el_name).attr($attr0, arg0).attr($attr1, arg1).attr($attr2, arg2).attr($attr3, arg3).attr($attr4, arg4)) + .expect("should write to buf"); + self + } + }; } macro_rules! closed_el_with_child { diff --git a/docx-core/src/xml_builder/mod.rs b/docx-core/src/xml_builder/mod.rs index 25839eb..f38a826 100644 --- a/docx-core/src/xml_builder/mod.rs +++ b/docx-core/src/xml_builder/mod.rs @@ -1,5 +1,6 @@ #[macro_use] mod macros; +mod core_properties; mod declaration; mod properties; mod relationship;