use ifc_model::Value;
use ifc_schema::Schema;
use super::enumeration;
use super::scalar::{describe_value, primitive_of, FixedWidth};
use super::select;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Mismatch {
Primitive {
expected: &'static str,
actual: &'static str,
},
EnumMember {
member: String,
declared: Vec<String>,
},
SelectMember {
written: String,
select: String,
},
FixedWidth {
expected: usize,
actual: usize,
},
}
#[must_use]
pub fn check(schema: &Schema, declared: &str, value: &Value) -> Option<Mismatch> {
match value {
Value::Null | Value::Derived => None,
Value::Enum(member) => match enumeration::is_member(schema, declared, member) {
Some(true) | None => None,
Some(false) => Some(Mismatch::EnumMember {
member: member.to_string(),
declared: enumeration::members(schema, declared)
.map(<[String]>::to_vec)
.unwrap_or_default(),
}),
},
Value::Typed { type_name, value } => {
if let Some(false) = select::accepts(schema, declared, type_name) {
return Some(Mismatch::SelectMember {
written: type_name.to_string(),
select: declared.to_string(),
});
}
check(schema, type_name, value)
}
Value::Ref(_) => None,
Value::List(_) => None,
Value::Text(text) => {
if let Some(width) = FixedWidth::from_resolved(&schema.resolve_defined(declared)) {
if !width.accepts(text) {
return Some(Mismatch::FixedWidth {
expected: width.0,
actual: text.chars().count(),
});
}
}
let primitive = primitive_of(schema, declared)?;
if primitive.accepts(value) {
None
} else {
Some(Mismatch::Primitive {
expected: primitive.describe(),
actual: describe_value(value),
})
}
}
other => {
let primitive = primitive_of(schema, declared)?;
if primitive.accepts(other) {
None
} else {
Some(Mismatch::Primitive {
expected: primitive.describe(),
actual: describe_value(other),
})
}
}
}
}