oox 0.1.0

Open Office XML file format deserializer
Documentation
use super::pml::{
    presentation::Presentation,
    slides::{Slide, SlideLayout, SlideMaster},
};
use crate::shared::{
    docprops::{AppInfo, Core},
    drawingml::sharedstylesheet::OfficeStyleSheet,
    relationship::{relationships_from_zip_file, Relationship},
};
use log::info;
use std::collections::HashMap;
use std::fs::File;
use std::path::{Path, PathBuf};
use zip::ZipArchive;

#[derive(Debug, Clone, PartialEq)]
pub struct Package {
    pub file_path: PathBuf,
    pub app: Option<Box<AppInfo>>,
    pub core: Option<Box<Core>>,
    pub presentation: Option<Box<Presentation>>,
    pub theme_map: HashMap<PathBuf, Box<OfficeStyleSheet>>,
    pub slide_master_map: HashMap<PathBuf, Box<SlideMaster>>,
    pub slide_layout_map: HashMap<PathBuf, Box<SlideLayout>>,
    pub slide_map: HashMap<PathBuf, Box<Slide>>,
    pub slide_master_rels_map: HashMap<PathBuf, Vec<Relationship>>,
    pub slide_layout_rels_map: HashMap<PathBuf, Vec<Relationship>>,
    pub slide_rels_map: HashMap<PathBuf, Vec<Relationship>>,
    pub medias: Vec<PathBuf>,
}

impl Package {
    pub fn from_file(pptx_path: &Path) -> Result<Self, Box<dyn (::std::error::Error)>> {
        let pptx_file = File::open(&pptx_path)?;
        let mut zipper = ZipArchive::new(&pptx_file)?;

        info!("parsing docProps/app.xml");
        let app = AppInfo::from_zip(&mut zipper).map(|val| val.into()).ok();
        info!("parsing docProps/core.xml");
        let core = Core::from_zip(&mut zipper).map(|val| val.into()).ok();
        info!("parsing ppt/presentation.xml");
        let presentation = Presentation::from_zip(&mut zipper).map(|val| val.into()).ok();
        let mut theme_map = HashMap::new();
        let mut slide_master_map = HashMap::new();
        let mut slide_layout_map = HashMap::new();
        let mut slide_map = HashMap::new();
        let mut slide_master_rels_map = HashMap::new();
        let mut slide_layout_rels_map = HashMap::new();
        let mut slide_rels_map = HashMap::new();
        let mut medias = Vec::new();

        for i in 0..zipper.len() {
            let mut zip_file = zipper.by_index(i)?;

            match PathBuf::from(zip_file.name()) {
                file_path if file_path.starts_with("ppt/theme") => {
                    info!("parsing theme file: {}", zip_file.name());
                    theme_map.insert(file_path, Box::new(OfficeStyleSheet::from_zip_file(&mut zip_file)?));
                }
                file_path if file_path.starts_with("ppt/slideMasters/_rels") => {
                    if file_path.extension().unwrap_or_default() != "rels" {
                        continue;
                    }

                    info!("parsing slide master relationship file: {}", zip_file.name());
                    slide_master_rels_map.insert(file_path, relationships_from_zip_file(&mut zip_file)?);
                }
                file_path if file_path.starts_with("ppt/slideMasters") => {
                    if file_path.extension().unwrap_or_default() != "xml" {
                        continue;
                    }

                    info!("parsing slide master file: {}", zip_file.name());
                    slide_master_map.insert(file_path, Box::new(SlideMaster::from_zip_file(&mut zip_file)?));
                }
                file_path if file_path.starts_with("ppt/slideLayouts/_rels") => {
                    if file_path.extension().unwrap_or_default() != "rels" {
                        continue;
                    }

                    info!("parsing slide layout relationship file: {}", zip_file.name());
                    slide_layout_rels_map.insert(file_path, relationships_from_zip_file(&mut zip_file)?);
                }
                file_path if file_path.starts_with("ppt/slideLayouts") => {
                    if file_path.extension().unwrap_or_default() != "xml" {
                        continue;
                    }

                    info!("parsing slide layout file: {}", zip_file.name());
                    slide_layout_map.insert(file_path, Box::new(SlideLayout::from_zip_file(&mut zip_file)?));
                }
                file_path if file_path.starts_with("ppt/slides/_rels") => {
                    if file_path.extension().unwrap_or_default() != "rels" {
                        continue;
                    }

                    info!("parsing slide relationship file: {}", zip_file.name());
                    slide_rels_map.insert(file_path, relationships_from_zip_file(&mut zip_file)?);
                }
                file_path if file_path.starts_with("ppt/slides") => {
                    if file_path.extension().unwrap_or_default() != "xml" {
                        continue;
                    }

                    info!("parsing slide file: {}", zip_file.name());
                    slide_map.insert(file_path, Box::new(Slide::from_zip_file(&mut zip_file)?));
                }
                file_path if file_path.starts_with("ppt/media") => {
                    medias.push(file_path);
                }
                _ => (),
            }
        }

        Ok(Self {
            file_path: PathBuf::from(pptx_path),
            app,
            core,
            presentation,
            theme_map,
            slide_master_map,
            slide_layout_map,
            slide_map,
            slide_master_rels_map,
            slide_layout_rels_map,
            slide_rels_map,
            medias,
        })
    }

    pub fn slides(&self) -> Slides {
        Slides::new(&self.slide_map)
    }
}
#[derive(Debug, Clone)]
pub struct Slides<'a> {
    slide_map: &'a HashMap<PathBuf, Box<Slide>>,
    current_page_num: usize,
}

impl<'a> Slides<'a> {
    pub fn new(slide_map: &'a HashMap<PathBuf, Box<Slide>>) -> Self {
        Self {
            slide_map,
            current_page_num: 1,
        }
    }
}

impl<'a> Iterator for Slides<'a> {
    type Item = &'a Slide;

    fn next(&mut self) -> Option<Self::Item> {
        for i in self.current_page_num..=self.slide_map.len() {
            let opt_slide = self.slide_map.get(&PathBuf::from(format!("ppt/slides/slide{}.xml", i)));
            self.current_page_num += 1;
            if let Some(slide) = opt_slide {
                return Some(slide);
            }
        }

        None
    }
}