jw-hwp-core 0.1.0

Read-only parser for Hancom HWP 5.0 (binary CFB) and HWPX (OWPML) documents
Documentation
//! ZIP container wrapper for HWPX parsing.

use crate::error::Error;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use zip::ZipArchive;

pub struct Archive {
    inner: ZipArchive<File>,
}

impl Archive {
    pub fn open(path: &Path) -> Result<Self, Error> {
        let file = File::open(path)?;
        let inner =
            ZipArchive::new(file).map_err(|e| Error::Container(format!("zip open: {e}")))?;
        Ok(Archive { inner })
    }

    pub fn read_entry(&mut self, name: &str) -> Result<Vec<u8>, Error> {
        let mut entry = self
            .inner
            .by_name(name)
            .map_err(|e| Error::MissingStream(format!("{name}: {e}")))?;
        let mut buf = Vec::with_capacity(entry.size() as usize);
        entry.read_to_end(&mut buf)?;
        Ok(buf)
    }

    pub fn has_entry(&self, name: &str) -> bool {
        self.inner.file_names().any(|n| n == name)
    }

    /// Return the uncompressed size of the named entry, or `None` if missing.
    pub fn entry_size(&mut self, name: &str) -> Option<u64> {
        self.inner.by_name(name).ok().map(|e| e.size())
    }

    pub fn list_entries(&self) -> Vec<String> {
        self.inner.file_names().map(|s| s.to_string()).collect()
    }

    pub fn validate_mimetype(&mut self) -> Result<(), Error> {
        let bytes = self.read_entry("mimetype")?;
        let s = std::str::from_utf8(&bytes)
            .map_err(|e| Error::Container(format!("mimetype not utf8: {e}")))?
            .trim();
        if s != "application/hwp+zip" {
            return Err(Error::InvalidHeader(format!(
                "unexpected HWPX mimetype: {s:?}"
            )));
        }
        Ok(())
    }
}