use arrow_array::{
builder::{make_builder, ArrayBuilder},
RecordBatch, RecordBatchOptions,
};
use arrow_schema::SchemaRef;
use crate::{
builders::{append, validate_capacity},
parse::parse,
Error, Result, XmlSchema,
};
pub struct XmlBatchBuilder {
schema: XmlSchema,
columns: Vec<Box<dyn ArrayBuilder>>,
batch_size: usize,
rows: usize,
}
impl XmlBatchBuilder {
pub fn new(schema: XmlSchema, batch_size: usize) -> Result<Self> {
if batch_size == 0 {
return Err(Error::Configuration("batch_size must be positive".into()));
}
let columns = schema
.fields
.iter()
.map(|f| make_builder(f.field.data_type(), batch_size.min(1024)))
.collect();
Ok(Self {
schema,
columns,
batch_size,
rows: 0,
})
}
pub fn new_projected(
schema: XmlSchema,
batch_size: usize,
projection: &[usize],
) -> Result<Self> {
Self::new(schema.project(projection)?, batch_size)
}
pub fn append_xml(&mut self, input: &str) -> Result<()> {
if self.is_full() {
return Err(Error::BatchFull);
}
let document = roxmltree::Document::parse(input)?;
let row = parse(&document, &self.schema)?;
for ((column, value), field) in self.columns.iter().zip(&row).zip(&self.schema.fields) {
validate_capacity(column.as_ref(), field, &[value])?;
}
for ((column, value), field) in self.columns.iter_mut().zip(row).zip(&self.schema.fields) {
append(column.as_mut(), field, value);
}
self.rows += 1;
Ok(())
}
pub fn len(&self) -> usize {
self.rows
}
pub fn is_empty(&self) -> bool {
self.rows == 0
}
pub fn is_full(&self) -> bool {
self.rows == self.batch_size
}
pub fn schema(&self) -> SchemaRef {
self.schema.arrow_schema()
}
pub fn flush(&mut self) -> Result<Option<RecordBatch>> {
if self.is_empty() {
return Ok(None);
}
let columns = self
.columns
.iter_mut()
.map(|column| column.finish())
.collect();
let options = RecordBatchOptions::new().with_row_count(Some(self.rows));
let batch = RecordBatch::try_new_with_options(self.schema(), columns, &options)?;
self.rows = 0;
Ok(Some(batch))
}
}