///|
/// A snapshot of literal values or stored formula caches, with zero-based row.
/// Mapping does not evaluate formulas or fill merged cells.
pub(all) struct TableRecord {
row : Int32
values : Map[String, Value]
}
///|
/// Exact, case-sensitive header names. Every header must be a nonblank Text
/// literal and unique. Rejects merged sheets rather than guessing fill-down.
/// Includes blank data rows; absent cells map to Empty, Text("") stays distinct.
pub fn Sheet::records(
self : Sheet,
header_row~ : Int32 = 0,
) -> Array[TableRecord] raise OdsError {
if header_row < 0 || header_row >= self.rows {
raise Invalid("header row outside sheet")
}
if !self.spans.is_empty() {
raise Invalid("record mapping requires an unmerged sheet")
}
let headers : Array[String] = []
for col in 0.. {
if cell.expression() is Some(_) {
raise Invalid("header must not be a formula")
}
match cell.value() {
Text(s) if s.trim() != "" => s
_ => raise Invalid("header must be nonblank text")
}
}
None => raise Invalid("missing header")
}
if headers.contains(name) {
raise Invalid("duplicate header: \{name}")
}
headers.push(name)
}
let result : Array[TableRecord] = []
for row = header_row + 1; row < self.rows; row = row + 1 {
let values : Map[String, Value] = Map([])
for col, name in headers {
values[name] = match self.get(row, col) {
Some(cell) => cell.value()
None => Empty
}
}
result.push({ row, values, })
}
result
}