use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use arrow_schema::{DataType, Field, Schema, SchemaRef};
use crate::{
validation::{validate_fields, validate_namespaces},
Error, Result,
};
#[derive(Clone, Debug)]
pub struct XmlField {
pub(crate) field: Field,
pub(crate) path: String,
pub(crate) children: Vec<XmlField>,
}
impl XmlField {
pub fn new(
name: impl Into<String>,
path: impl Into<String>,
data_type: DataType,
nullable: bool,
) -> Self {
Self {
field: Field::new(name, data_type, nullable),
path: path.into(),
children: Vec::new(),
}
}
pub fn structure(
name: impl Into<String>,
path: impl Into<String>,
nullable: bool,
children: Vec<XmlField>,
) -> Self {
let data_type =
DataType::Struct(children.iter().map(|child| child.field.clone()).collect());
Self {
field: Field::new(name, data_type, nullable),
path: path.into(),
children,
}
}
pub fn list(
name: impl Into<String>,
path: impl Into<String>,
nullable: bool,
item: XmlField,
) -> Self {
Self {
field: Field::new(name, DataType::List(Arc::new(item.field.clone())), nullable),
path: path.into(),
children: vec![item],
}
}
pub fn arrow_field(&self) -> &Field {
&self.field
}
pub fn path(&self) -> &str {
&self.path
}
pub fn children(&self) -> &[XmlField] {
&self.children
}
pub(crate) fn error(&self, message: impl Into<String>) -> Error {
Error::Field {
name: self.field.name().clone(),
path: self.path.clone(),
message: message.into(),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct NamespaceBinding<'a> {
pub prefix: &'a str,
pub uri: &'a str,
}
#[derive(Clone, Debug)]
pub struct XmlSchema {
pub(crate) fields: Vec<XmlField>,
arrow: SchemaRef,
pub(crate) namespaces: HashMap<String, String>,
}
impl XmlSchema {
pub fn new(fields: Vec<XmlField>) -> Result<Self> {
Self::with_namespaces(fields, &[])
}
pub fn with_namespaces(
fields: Vec<XmlField>,
bindings: &[NamespaceBinding<'_>],
) -> Result<Self> {
let namespaces = validate_namespaces(bindings)?;
validate_fields(&fields, &namespaces)?;
let arrow = Arc::new(Schema::new(
fields.iter().map(|f| f.field.clone()).collect::<Vec<_>>(),
));
Ok(Self {
fields,
arrow,
namespaces,
})
}
pub fn fields(&self) -> &[XmlField] {
&self.fields
}
pub fn arrow_schema(&self) -> SchemaRef {
Arc::clone(&self.arrow)
}
pub(crate) fn project(&self, projection: &[usize]) -> Result<Self> {
let mut seen = HashSet::new();
let mut fields = Vec::with_capacity(projection.len());
for &index in projection {
let field = self.fields.get(index).ok_or_else(|| {
Error::Configuration(format!("projection index {index} is out of bounds"))
})?;
if !seen.insert(index) {
return Err(Error::Configuration(format!(
"duplicate projection index {index}"
)));
}
fields.push(field.clone());
}
let arrow = Arc::new(self.arrow.project(projection)?);
Ok(Self {
fields,
arrow,
namespaces: self.namespaces.clone(),
})
}
}