use copybook_core::schema::{Field, FieldKind};
use crate::builders::ColumnAccumulator;
use crate::options::ArrowOptions;
use crate::{ArrowError, Result};
#[inline]
pub(crate) fn decode_record_to_columns(
schema: ©book_core::Schema,
record: &[u8],
accumulators: &mut [Box<dyn ColumnAccumulator>],
options: &ArrowOptions,
) -> Result<()> {
let mut idx = 0;
for field in &schema.fields {
dispatch_field(field, record, accumulators, &mut idx, options)?;
}
if idx != accumulators.len() {
return Err(ArrowError::ColumnBuild(format!(
"Schema/accumulator mismatch: dispatched {idx} fields but have {} accumulators",
accumulators.len()
)));
}
Ok(())
}
fn dispatch_field(
field: &Field,
record: &[u8],
accumulators: &mut [Box<dyn ColumnAccumulator>],
idx: &mut usize,
options: &ArrowOptions,
) -> Result<()> {
if matches!(
field.kind,
FieldKind::Condition { .. } | FieldKind::Renames { .. }
) {
return Ok(());
}
if is_filler(field) && !options.emit_filler {
return Ok(());
}
if matches!(field.kind, FieldKind::Group) {
if options.flatten_groups {
for child in &field.children {
dispatch_field(child, record, accumulators, idx, options)?;
}
}
return Ok(());
}
if *idx >= accumulators.len() {
return Err(ArrowError::ColumnBuild(format!(
"Accumulator index {} out of bounds (have {}), field '{}'",
*idx,
accumulators.len(),
field.name
)));
}
let offset = field.offset as usize;
let len = field.len as usize;
let end = offset + len;
if end <= record.len() {
accumulators[*idx].append_value(&record[offset..end])?;
} else {
accumulators[*idx].append_null();
}
*idx += 1;
Ok(())
}
fn is_filler(field: &Field) -> bool {
field.name.starts_with("_filler_") || field.name.eq_ignore_ascii_case("FILLER")
}