1use std::{
2 collections::{HashMap, HashSet},
3 sync::Arc,
4};
5
6use arrow_schema::{DataType, Field, Schema, SchemaRef};
7
8use crate::{
9 validation::{validate_fields, validate_namespaces},
10 Error, Result,
11};
12
13#[derive(Clone, Debug)]
15pub struct XmlField {
16 pub(crate) field: Field,
17 pub(crate) path: String,
18 pub(crate) children: Vec<XmlField>,
19}
20
21impl XmlField {
22 pub fn new(
24 name: impl Into<String>,
25 path: impl Into<String>,
26 data_type: DataType,
27 nullable: bool,
28 ) -> Self {
29 Self {
30 field: Field::new(name, data_type, nullable),
31 path: path.into(),
32 children: Vec::new(),
33 }
34 }
35
36 pub fn structure(
38 name: impl Into<String>,
39 path: impl Into<String>,
40 nullable: bool,
41 children: Vec<XmlField>,
42 ) -> Self {
43 let data_type =
44 DataType::Struct(children.iter().map(|child| child.field.clone()).collect());
45 Self {
46 field: Field::new(name, data_type, nullable),
47 path: path.into(),
48 children,
49 }
50 }
51
52 pub fn list(
55 name: impl Into<String>,
56 path: impl Into<String>,
57 nullable: bool,
58 item: XmlField,
59 ) -> Self {
60 Self {
61 field: Field::new(name, DataType::List(Arc::new(item.field.clone())), nullable),
62 path: path.into(),
63 children: vec![item],
64 }
65 }
66
67 pub fn arrow_field(&self) -> &Field {
69 &self.field
70 }
71
72 pub fn path(&self) -> &str {
74 &self.path
75 }
76
77 pub fn children(&self) -> &[XmlField] {
79 &self.children
80 }
81
82 pub(crate) fn error(&self, message: impl Into<String>) -> Error {
83 Error::Field {
84 name: self.field.name().clone(),
85 path: self.path.clone(),
86 message: message.into(),
87 }
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
93pub struct NamespaceBinding<'a> {
94 pub prefix: &'a str,
96 pub uri: &'a str,
98}
99
100#[derive(Clone, Debug)]
102pub struct XmlSchema {
103 pub(crate) fields: Vec<XmlField>,
104 arrow: SchemaRef,
105 pub(crate) namespaces: HashMap<String, String>,
106}
107
108impl XmlSchema {
109 pub fn new(fields: Vec<XmlField>) -> Result<Self> {
111 Self::with_namespaces(fields, &[])
112 }
113
114 pub fn with_namespaces(
117 fields: Vec<XmlField>,
118 bindings: &[NamespaceBinding<'_>],
119 ) -> Result<Self> {
120 let namespaces = validate_namespaces(bindings)?;
121 validate_fields(&fields, &namespaces)?;
122 let arrow = Arc::new(Schema::new(
123 fields.iter().map(|f| f.field.clone()).collect::<Vec<_>>(),
124 ));
125 Ok(Self {
126 fields,
127 arrow,
128 namespaces,
129 })
130 }
131
132 pub fn fields(&self) -> &[XmlField] {
134 &self.fields
135 }
136
137 pub fn arrow_schema(&self) -> SchemaRef {
139 Arc::clone(&self.arrow)
140 }
141
142 pub(crate) fn validate_projection(&self, projection: &[usize]) -> Result<()> {
143 let mut seen = HashSet::new();
144 for &index in projection {
145 if index >= self.fields.len() {
146 return Err(Error::Configuration(format!(
147 "projection index {index} is out of bounds"
148 )));
149 }
150 if !seen.insert(index) {
151 return Err(Error::Configuration(format!(
152 "duplicate projection index {index}"
153 )));
154 }
155 }
156 Ok(())
157 }
158
159 pub(crate) fn project(&self, projection: &[usize]) -> Result<Self> {
160 self.validate_projection(projection)?;
161 let fields = projection
162 .iter()
163 .map(|&index| self.fields[index].clone())
164 .collect();
165 let arrow = Arc::new(self.arrow.project(projection)?);
166 Ok(Self {
167 fields,
168 arrow,
169 namespaces: self.namespaces.clone(),
170 })
171 }
172}