rxlsb 0.3.0

Pure Rust XLSB (Excel Binary Workbook) reader/writer library
Documentation
use crate::io::BufferReader;
use crate::format::RecordType;
use crate::error::Result;
use bytes::Bytes;

#[derive(Debug, Clone)]
pub enum CellValue {
    Text(String),
    Number(f64),
    Bool(bool),
    #[allow(dead_code)]
    Blank,
}

#[derive(Debug, Clone)]
pub struct CellInfo {
    pub row: u32,
    pub col: u32,
    pub value: CellValue,
    pub style_index: u32,
}

#[derive(Debug, Clone)]
pub struct MergeCell {
    pub row_first: u32,
    pub row_last: u32,
    pub col_first: u32,
    pub col_last: u32,
}

pub struct SheetParser {
    pub cells: Vec<CellInfo>,
    pub merges: Vec<MergeCell>,
    pub max_row: u32,
    pub max_col: u32,
}

impl SheetParser {
    pub fn parse(data: &[u8], sst_strings: &[&str]) -> Self {
        let result = Self::parse_with_reader(Bytes::copy_from_slice(data), sst_strings);
        result.unwrap_or_else(|_| Self {
            cells: Vec::new(),
            merges: Vec::new(),
            max_row: 0,
            max_col: 0,
        })
    }
    
    fn parse_with_reader(data: Bytes, sst_strings: &[&str]) -> Result<Self> {
        let mut reader = BufferReader::new(data);
        let mut cells = Vec::with_capacity(256);
        let mut merges = Vec::with_capacity(16);
        let mut max_row = 0u32;
        let mut max_col = 0u32;
        
        while reader.has_remaining() {
            let record_type_code = reader.read_varint()?;
            let size = reader.read_varsize()?;
            
            let record_type = RecordType::from_u32(record_type_code);
            
            match record_type {
                Some(RecordType::BrtRowHdr) => {
                    let row = reader.read_u32_le()?;
                    max_row = max_row.max(row);
                    reader.skip(size as usize - 4)?;
                }
                
                Some(RecordType::BrtCellBlank) => {
                    let col = reader.read_u32_le()?;
                    let _style_index = reader.read_u32_le()?;
                    reader.skip(size as usize - 8)?;
                    
                    max_col = max_col.max(col);
                }
                
                Some(RecordType::BrtCellRk) => {
                    let col = reader.read_u32_le()?;
                    let style_index = reader.read_u32_le()?;
                    let rk_bytes = reader.read_u32_le()?;
                    
                    max_col = max_col.max(col);
                    
                    let value = Self::decode_rk(rk_bytes);
                    cells.push(CellInfo {
                        row: max_row,
                        col,
                        value: CellValue::Number(value),
                        style_index,
                    });
                }
                
                Some(RecordType::BrtCellReal) => {
                    let col = reader.read_u32_le()?;
                    let style_index = reader.read_u32_le()?;
                    let value = reader.read_f64_le()?;
                    
                    max_col = max_col.max(col);
                    cells.push(CellInfo {
                        row: max_row,
                        col,
                        value: CellValue::Number(value),
                        style_index,
                    });
                }
                
                Some(RecordType::BrtCellBool) => {
                    let col = reader.read_u32_le()?;
                    let style_index = reader.read_u32_le()?;
                    let bool_value = reader.read_u8()?;
                    reader.skip(size as usize - 9)?;
                    
                    max_col = max_col.max(col);
                    cells.push(CellInfo {
                        row: max_row,
                        col,
                        value: CellValue::Bool(bool_value != 0),
                        style_index,
                    });
                }
                
                Some(RecordType::BrtCellIsst) => {
                    let col = reader.read_u32_le()?;
                    let style_index = reader.read_u32_le()?;
                    let sst_index = reader.read_u32_le()?;
                    reader.skip(size as usize - 12)?;
                    
                    max_col = max_col.max(col);
                    
                    let text = sst_strings.get(sst_index as usize)
                        .map(|s| s.to_string())
                        .unwrap_or_default();
                    
                    cells.push(CellInfo {
                        row: max_row,
                        col,
                        value: CellValue::Text(text),
                        style_index,
                    });
                }
                
                Some(RecordType::BrtCellSt) => {
                    let col = reader.read_u32_le()?;
                    let style_index = reader.read_u32_le()?;
                    
                    reader.skip(1)?;
                    let char_count = reader.read_u32_le()? as usize;
                    let mut chars = Vec::with_capacity(char_count);
                    for _ in 0..char_count {
                        chars.push(reader.read_u16_le()?);
                    }
                    
                    let text = String::from_utf16(&chars).unwrap_or_default();
                    
                    max_col = max_col.max(col);
                    cells.push(CellInfo {
                        row: max_row,
                        col,
                        value: CellValue::Text(text),
                        style_index,
                    });
                }
                
                Some(RecordType::BrtMergeCell) => {
                    let row_first = reader.read_u32_le()?;
                    let row_last = reader.read_u32_le()?;
                    let col_first = reader.read_u32_le()?;
                    let col_last = reader.read_u32_le()?;
                    
                    merges.push(MergeCell {
                        row_first,
                        row_last,
                        col_first,
                        col_last,
                    });
                    
                    max_row = max_row.max(row_last);
                    max_col = max_col.max(col_last);
                }
                
                Some(RecordType::BrtEndSheetData) => {
                    break;
                }
                
                _ => {
                    reader.skip(size as usize)?;
                }
            }
        }
        
        Ok(Self {
            cells,
            merges,
            max_row,
            max_col,
        })
    }
    
    fn decode_rk(rk: u32) -> f64 {
        let is_int = (rk & 0x02) != 0;
        let div_100 = (rk & 0x01) != 0;
        
        if is_int {
            let value = ((rk as i32) >> 2) as f64;
            if div_100 {
                value / 100.0
            } else {
                value
            }
        } else {
            let bits = ((rk as u64 & 0xFFFFFFFC) as u64) << 32;
            let value = f64::from_bits(bits);
            if div_100 {
                value / 100.0
            } else {
                value
            }
        }
    }
    
    pub fn get_cells(&self) -> &[CellInfo] {
        &self.cells
    }
    
    pub fn get_merges(&self) -> &[MergeCell] {
        &self.merges
    }
    
    pub fn find_text_cell(&self, marker: &str) -> Option<(u32, u32)> {
        for cell in &self.cells {
            if let CellValue::Text(text) = &cell.value {
                if text == marker {
                    return Some((cell.row, cell.col));
                }
            }
        }
        None
    }
}