use xlsbye_core::error::Result;
use xlsbye_core::types::ParsedChartsheet;
use crate::record::header::RecordIter;
use crate::strings::{decode_short_string, decode_short_string_u16, decode_wide_string};
pub fn parse_chartsheet(data: &[u8]) -> Result<ParsedChartsheet> {
let mut drawing_rel_id = None;
for record in RecordIter::new(data) {
let (_record_type, payload) = record?;
if drawing_rel_id.is_none() {
drawing_rel_id = extract_rel_id(payload);
}
}
Ok(ParsedChartsheet { drawing_rel_id })
}
fn extract_rel_id(payload: &[u8]) -> Option<String> {
for offset in 0..payload.len() {
if let Some((value, _)) = decode_wide_string(&payload[offset..]) {
if is_rid(&value) {
return Some(value);
}
}
if let Some((value, _)) = decode_short_string(&payload[offset..]) {
if is_rid(&value) {
return Some(value);
}
}
if let Some((value, _)) = decode_short_string_u16(&payload[offset..]) {
if is_rid(&value) {
return Some(value);
}
}
}
None
}
fn is_rid(value: &str) -> bool {
let Some(suffix) = value.strip_prefix("rId") else {
return false;
};
!suffix.is_empty() && suffix.chars().all(|ch| ch.is_ascii_digit())
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_varint(mut value: u32) -> Vec<u8> {
let mut out = Vec::new();
loop {
let mut byte = (value & 0x7F) as u8;
value >>= 7;
if value != 0 {
byte |= 0x80;
}
out.push(byte);
if value == 0 {
break;
}
}
out
}
fn encode_record(record_type: u16, payload: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&encode_varint(u32::from(record_type)));
out.extend_from_slice(&encode_varint(payload.len() as u32));
out.extend_from_slice(payload);
out
}
fn encode_wide_string(value: &str) -> Vec<u8> {
let utf16 = value.encode_utf16().collect::<Vec<_>>();
let mut out = Vec::new();
out.extend_from_slice(&(utf16.len() as u32).to_le_bytes());
for unit in utf16 {
out.extend_from_slice(&unit.to_le_bytes());
}
out
}
#[test]
fn parses_chartsheet_drawing_relationship_id() {
let mut payload = Vec::new();
payload.extend_from_slice(&0u32.to_le_bytes());
payload.extend_from_slice(&encode_wide_string("rId1"));
let mut data = Vec::new();
data.extend_from_slice(&encode_record(0x1234, &payload));
let parsed = parse_chartsheet(&data).expect("chartsheet should parse");
assert_eq!(parsed.drawing_rel_id.as_deref(), Some("rId1"));
}
}