use std::collections::BTreeMap;
use qubit_datatype::DataType;
use qubit_value::Value;
use serde::{
Deserialize,
Serialize,
};
use crate::schema::{
MetadataField,
MetadataSchemaBuilder,
UnknownFieldPolicy,
};
use crate::{
Metadata,
MetadataError,
MetadataResult,
MetadataValidationError,
MetadataValidationResult,
};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MetadataSchema {
fields: BTreeMap<String, MetadataField>,
unknown_field_policy: UnknownFieldPolicy,
}
impl MetadataSchema {
#[inline]
#[must_use]
pub fn builder() -> MetadataSchemaBuilder {
MetadataSchemaBuilder::default()
}
#[inline]
pub(crate) fn new(
fields: BTreeMap<String, MetadataField>,
unknown_field_policy: UnknownFieldPolicy,
) -> Self {
Self {
fields,
unknown_field_policy,
}
}
#[inline]
#[must_use]
pub fn field(&self, key: &str) -> Option<&MetadataField> {
self.fields.get(key)
}
#[inline]
#[must_use]
pub fn field_type(&self, key: &str) -> Option<DataType> {
self.field(key).map(MetadataField::data_type)
}
#[inline]
#[must_use]
pub fn unknown_field_policy(&self) -> UnknownFieldPolicy {
self.unknown_field_policy
}
#[inline]
pub fn fields(&self) -> impl Iterator<Item = (&str, &MetadataField)> {
self.fields.iter().map(|(key, field)| (key.as_str(), field))
}
pub fn validate(&self, meta: &Metadata) -> MetadataValidationResult<()> {
let mut issues = Vec::new();
for (key, field) in &self.fields {
if field.is_required() && !meta.contains_key(key) {
issues.push(MetadataError::MissingRequiredField {
key: key.clone(),
expected: field.data_type(),
});
}
}
for (key, value) in meta.iter() {
if let Err(error) = self.validate_entry(key, value) {
issues.push(error);
}
}
if let Some(error) = MetadataValidationError::from_issues(issues) {
Err(error)
} else {
Ok(())
}
}
pub(crate) fn validate_entry(&self, key: &str, value: &Value) -> MetadataResult<()> {
match self.field(key) {
Some(field) if field.data_type() != value.data_type() => Err(
MetadataError::type_mismatch(key, field.data_type(), value.data_type()),
),
Some(_) => Ok(()),
None if matches!(self.unknown_field_policy, UnknownFieldPolicy::Reject) => {
Err(MetadataError::UnknownField {
key: key.to_string(),
})
}
None => Ok(()),
}
}
}
impl Default for MetadataSchema {
#[inline]
fn default() -> Self {
Self {
fields: BTreeMap::new(),
unknown_field_policy: UnknownFieldPolicy::Reject,
}
}
}