use crate::{NamespaceBinding, XmlSchema};
use arrow_array::RecordBatch;
use arrow_schema::SchemaRef;
use futures_util::{
io::AsyncBufRead,
stream::{self, BoxStream},
StreamExt, TryStreamExt,
};
mod error;
mod inference;
mod options;
mod state;
mod validation;
pub use error::{Limit, ReadError, Result as ReaderResult};
pub use options::{InferenceOptions, ReaderOptions};
use error::Result;
use state::ReaderState;
#[derive(PartialEq, Eq)]
struct Name {
uri: Option<String>,
local: String,
}
#[derive(Debug)]
pub struct XmlRecord {
pub xml: String,
pub byte_offset: u64,
}
pub fn read_records<'a, R: AsyncBufRead + Unpin + Send + 'a>(
input: R,
schema: XmlSchema,
record_path: &str,
options: ReaderOptions,
) -> Result<BoxStream<'a, Result<XmlRecord>>> {
options.validate(&schema, record_path)?;
let schema = schema.project(&[]).map_err(ReadError::configuration)?;
let options = ReaderOptions {
projection: None,
..options
};
let state = ReaderState::new(input, schema, record_path, options)?;
Ok(record_stream(state))
}
pub async fn infer_records<'a, R: AsyncBufRead + Unpin + Send + 'a>(
input: R,
record_path: &str,
bindings: &[NamespaceBinding<'_>],
options: ReaderOptions,
sampling: InferenceOptions,
) -> Result<(XmlSchema, BoxStream<'a, Result<XmlRecord>>)> {
let (state, schema) =
ReaderState::infer(input, record_path, bindings, options, sampling).await?;
Ok((schema, record_stream(state)))
}
fn record_stream<'a, R: AsyncBufRead + Unpin + Send + 'a>(
state: ReaderState<R>,
) -> BoxStream<'a, Result<XmlRecord>> {
stream::try_unfold((state, Vec::new()), |(mut state, mut buffer)| async move {
Ok(state
.next_replay_record(&mut buffer)
.await?
.map(|record| (record, (state, buffer))))
})
.boxed()
}
pub struct XmlReader<'a> {
batches: BoxStream<'a, Result<RecordBatch>>,
schema: SchemaRef,
}
impl<'a> XmlReader<'a> {
pub fn new<R: AsyncBufRead + Unpin + Send + 'a>(
input: R,
schema: XmlSchema,
record_path: &str,
options: ReaderOptions,
) -> Result<Self> {
Ok(Self::from_state(ReaderState::new(
input,
schema,
record_path,
options,
)?))
}
pub async fn infer<R: AsyncBufRead + Unpin + Send + 'a>(
input: R,
record_path: &str,
bindings: &[NamespaceBinding<'_>],
options: ReaderOptions,
sampling: InferenceOptions,
) -> Result<Self> {
let (state, schema) =
ReaderState::infer(input, record_path, bindings, options, sampling).await?;
Ok(Self::from_state(state.with_schema(schema)?))
}
fn from_state<R: AsyncBufRead + Unpin + Send + 'a>(state: ReaderState<R>) -> Self {
let schema = state.schema();
let batches = stream::try_unfold(state, |mut state| async move {
Ok(state.next_batch().await?.map(|batch| (batch, state)))
})
.boxed();
Self { batches, schema }
}
pub fn schema(&self) -> SchemaRef {
self.schema.clone()
}
pub async fn next_batch(&mut self) -> Result<Option<RecordBatch>> {
self.batches.try_next().await
}
pub fn into_stream(self) -> BoxStream<'a, Result<RecordBatch>> {
self.batches
}
}