ooxml 0.1.1

An Office OpenXML parser writen in Rust
Documentation
mod reference;

pub use reference::ReferenceRelationship;
pub struct RelationshipId(String);
pub struct ExternalRelationship(ReferenceRelationship);
pub struct DataPartReferenceRelationship(ReferenceRelationship);

pub struct HyperlinkRelationship(ReferenceRelationship);

pub struct DataPartReferenceRelationships {}
pub struct ExternalRelationships {}

pub struct HyperlinkRelationships {}

use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

use linked_hash_map::LinkedHashMap;
use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};

use crate::error::OoxmlError;

pub const RELATIONSHIPS_FILE: &'static str = "_rels/.rels";

const XMLNS_ATTRIBUTE_NAME: &'static str = "xmlns";
const RELATIONSHIP_NAMESPACE_URI: &'static str =
    "http://schemas.openxmlformats.org/package/2006/relationships";
// const XMLNS_R_ATTRIBUTE_NAME: &'static str = "xmlns:r";
const RELATIONSHIP_TAG_NAME: &'static str = "Relationship";
const RELATIONSHIPS_TAG_NAME: &'static str = "Relationships";

#[derive(Debug, PartialEq, Default, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct Relationship {
    id: String,
    r#type: String,
    target: String,
}

impl Relationship {
    pub fn id(&self) -> &str {
        &self.id
    }
    pub fn r#type(&self) -> &str {
        &self.r#type
    }
    pub fn target(&self) -> &str {
        &self.target
    }
}
#[derive(Debug, PartialEq, Default, Clone)]
pub struct Relationships {
    relationships: LinkedHashMap<String, Relationship>,
}

struct RelationshipsVisitor;

impl<'de> Visitor<'de> for RelationshipsVisitor {
    // The type that our Visitor is going to produce.
    type Value = Relationships;

    // Format a message stating what data this Visitor expects to receive.
    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("relationships deserializing error")
    }

    // Deserialize MyMap from an abstract "map" provided by the
    // Deserializer. The MapAccess input is a callback provided by
    // the Deserializer to let us see each entry in the map.
    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut types: Relationships = Relationships::default();
        while let Some(key) = access.next_key()? {
            let key: String = key;
            match &key {
                s if s == XMLNS_ATTRIBUTE_NAME => {
                    let _xmlns: String = access.next_value()?;
                }
                s if s == RELATIONSHIPS_TAG_NAME => {
                    unreachable!();
                }
                s if s == RELATIONSHIP_TAG_NAME => {
                    let v: Relationship = access.next_value()?;
                    types.add_relationship(v);
                    //types.add_default_element(v.extension, v.content_type);
                }
                _ => {
                    println!("{}", key);
                    unreachable!("content type unsupport!");
                }
            }
        }
        Ok(types)
    }
}
impl<'de> Deserialize<'de> for Relationships {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        // Instantiate our Visitor and ask the Deserializer to drive
        // it over the input data, resulting in an instance of MyMap.
        deserializer.deserialize_map(RelationshipsVisitor)
    }
}

impl fmt::Display for Relationships {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut container = Vec::new();
        let mut cursor = std::io::Cursor::new(&mut container);
        self.write(&mut cursor).expect("write xml to memory error");
        let s = String::from_utf8_lossy(&container);
        write!(f, "{}", s)?;
        // /write!("{}", String::from_utf8(cursor.into_inner().into));
        Ok(())
    }
}
impl Relationships {
    /// Parse content types data from an xml reader.
    pub fn parse_from_xml_reader<R: BufRead>(reader: R) -> Self {
        quick_xml::de::from_reader(reader).unwrap()
    }

    /// Parse content types data from an xml str.
    pub fn parse_from_xml_str(reader: &str) -> Self {
        quick_xml::de::from_str(reader).unwrap()
    }

    /// Save to file path.
    pub fn save_as<P: AsRef<Path>>(&self, path: P) -> Result<(), OoxmlError> {
        let file = File::create(path)?;
        self.write(file)
    }

    /// Write to an writer
    pub fn write<W: std::io::Write>(&self, writer: W) -> Result<(), OoxmlError> {
        let mut xml = quick_xml::Writer::new(writer);
        use quick_xml::events::attributes::Attribute;
        use quick_xml::events::*;

        // 1. write decl
        xml.write_event(Event::Decl(BytesDecl::new(
            b"1.0",
            Some(b"UTF-8"),
            Some(b"yes"),
        )))?;

        // 2. start types element
        let mut elem = BytesStart::borrowed_name(RELATIONSHIPS_TAG_NAME.as_bytes());
        let ns = Attribute {
            key: XMLNS_ATTRIBUTE_NAME.as_bytes(),
            value: RELATIONSHIP_NAMESPACE_URI.as_bytes().into(),
        };
        elem.extend_attributes(vec![ns]);
        xml.write_event(Event::Start(elem))?;

        // 3. write default entries
        for (_key, value) in &self.relationships {
            quick_xml::se::to_writer(xml.inner(), value).expect("[inernal] serde to xml error");
        }

        // ends types element.
        let end = BytesEnd::borrowed(RELATIONSHIPS_TAG_NAME.as_bytes());
        xml.write_event(Event::End(end))?;
        Ok(())
    }

    pub fn add_relationship(&mut self, relationship: Relationship) {
        self.relationships
            .insert(relationship.id.clone(), relationship);
    }

    pub fn get_relationship_by_id(&self, id: &str) -> Option<&Relationship> {
        self.relationships.get(id)
    }

    pub fn is_empty(&self) -> bool {
        self.relationships.is_empty()
    }

    pub fn contains(&self, id: &str) -> bool {
        self.relationships.contains_key(id)
    }
}

#[test]
fn test_de() {
    let raw = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties" Target="docProps/custom.xml"/></Relationships>"#;
    println!("{}", raw);
    let relationships: Relationships = quick_xml::de::from_str(raw).unwrap();
    println!("{:?}", relationships);
    let display = format!("{}", relationships);
    assert_eq!(raw, display);
}