use std::collections::HashMap;
use crate::error::{ArrowError, Result};
use super::Field;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Schema {
pub(crate) fields: Vec<Field>,
pub(crate) metadata: HashMap<String, String>,
}
impl Schema {
pub fn empty() -> Self {
Self {
fields: vec![],
metadata: HashMap::new(),
}
}
pub fn new(fields: Vec<Field>) -> Self {
Self::new_from(fields, HashMap::new())
}
#[inline]
pub const fn new_from(fields: Vec<Field>, metadata: HashMap<String, String>) -> Self {
Self { fields, metadata }
}
#[inline]
pub fn with_metadata(self, metadata: HashMap<String, String>) -> Self {
Self {
fields: self.fields,
metadata,
}
}
pub fn try_merge(schemas: impl IntoIterator<Item = Self>) -> Result<Self> {
schemas
.into_iter()
.try_fold(Self::empty(), |mut merged, schema| {
let Schema { metadata, fields } = schema;
for (key, value) in metadata.into_iter() {
if let Some(old_val) = merged.metadata.get(&key) {
if old_val != &value {
return Err(ArrowError::InvalidArgumentError(
"Fail to merge schema due to conflicting metadata.".to_string(),
));
}
}
merged.metadata.insert(key, value);
}
for field in fields.into_iter() {
let mut new_field = true;
for merged_field in &mut merged.fields {
if field.name() != merged_field.name() {
continue;
}
new_field = false;
merged_field.try_merge(&field)?
}
if new_field {
merged.fields.push(field);
}
}
Ok(merged)
})
}
#[inline]
pub const fn fields(&self) -> &Vec<Field> {
&self.fields
}
pub fn field(&self, i: usize) -> &Field {
&self.fields[i]
}
pub fn field_with_name(&self, name: &str) -> Result<&Field> {
Ok(&self.fields[self.index_of(name)?])
}
pub fn index_of(&self, name: &str) -> Result<usize> {
for i in 0..self.fields.len() {
if self.fields[i].name() == name {
return Ok(i);
}
}
let valid_fields: Vec<String> = self.fields.iter().map(|f| f.name().clone()).collect();
Err(ArrowError::InvalidArgumentError(format!(
"Unable to get field named \"{}\". Valid fields: {:?}",
name, valid_fields
)))
}
#[inline]
pub const fn metadata(&self) -> &HashMap<String, String> {
&self.metadata
}
pub fn column_with_name(&self, name: &str) -> Option<(usize, &Field)> {
self.fields
.iter()
.enumerate()
.find(|&(_, c)| c.name() == name)
}
}
impl std::fmt::Display for Schema {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(
&self
.fields
.iter()
.map(|c| c.to_string())
.collect::<Vec<String>>()
.join(", "),
)
}
}