hwpers 0.5.0

A Rust library for parsing Korean Hangul Word Processor (HWP) files with full layout rendering support
Documentation
use crate::error::{HwpError, Result};
use cfb::CompoundFile;
use std::io::{Read, Seek};
use std::path::Path;

pub struct CfbReader<F> {
    cfb: CompoundFile<F>,
}

impl CfbReader<std::fs::File> {
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
        let file = std::fs::File::open(path)?;
        let cfb = CompoundFile::open(file)
            .map_err(|e| HwpError::Cfb(format!("Failed to open CFB: {e}")))?;
        Ok(Self { cfb })
    }
}

impl<F: Read + Seek> CfbReader<F> {
    pub fn new(reader: F) -> Result<Self> {
        let cfb = CompoundFile::open(reader)
            .map_err(|e| HwpError::Cfb(format!("Failed to open CFB: {e}")))?;
        Ok(Self { cfb })
    }

    pub fn read_stream(&mut self, path: &str) -> Result<Vec<u8>> {
        let mut stream = self
            .cfb
            .open_stream(path)
            .map_err(|e| HwpError::NotFound(format!("Stream '{path}' not found: {e}")))?;

        let mut buffer = Vec::new();
        stream.read_to_end(&mut buffer)?;
        Ok(buffer)
    }

    pub fn stream_exists(&self, path: &str) -> bool {
        self.cfb.exists(path)
    }

    pub fn list_streams(&self) -> Vec<String> {
        let mut streams = Vec::new();
        for entry in self.cfb.walk() {
            if entry.is_stream() {
                streams.push(entry.path().display().to_string());
            }
        }
        streams
    }
}