use crate::error::{Result, SpssError};
#[derive(Debug, Clone)]
pub struct LongStringLabelSet {
pub var_name: String,
pub labels: Vec<(Vec<u8>, Vec<u8>)>,
}
pub fn parse_long_string_labels(data: &[u8]) -> Result<Vec<LongStringLabelSet>> {
let mut result = Vec::new();
let mut pos = 0;
while pos + 4 <= data.len() {
let name_len = read_i32_le(data, pos)? as usize;
pos += 4;
if pos + name_len > data.len() {
break;
}
let var_name = String::from_utf8_lossy(&data[pos..pos + name_len])
.trim()
.to_string();
pos += name_len;
if pos + 4 > data.len() {
break;
}
pos += 4;
if pos + 4 > data.len() {
break;
}
let label_count = read_i32_le(data, pos)? as usize;
pos += 4;
let mut labels = Vec::with_capacity(label_count);
for _ in 0..label_count {
if pos + 4 > data.len() {
break;
}
let value_len = read_i32_le(data, pos)? as usize;
pos += 4;
if pos + value_len > data.len() {
break;
}
let value = data[pos..pos + value_len].to_vec();
pos += value_len;
if pos + 4 > data.len() {
break;
}
let label_len = read_i32_le(data, pos)? as usize;
pos += 4;
if pos + label_len > data.len() {
break;
}
let label = data[pos..pos + label_len].to_vec();
pos += label_len;
labels.push((value, label));
}
result.push(LongStringLabelSet { var_name, labels });
}
Ok(result)
}
fn read_i32_le(data: &[u8], pos: usize) -> Result<i32> {
if pos + 4 > data.len() {
return Err(SpssError::TruncatedFile {
expected: pos + 4,
actual: data.len(),
});
}
let bytes: [u8; 4] = data[pos..pos + 4].try_into().unwrap();
Ok(i32::from_le_bytes(bytes))
}