use crate::parser::entity::Attribute;
#[derive(Debug, Clone, PartialEq)]
pub enum ConvertError {
AttributeCount {
entity_id: u64,
entity_name: String,
expected: usize,
actual: usize,
},
AttributeType {
entity_id: u64,
field_name: &'static str,
expected: &'static str,
actual: AttributeKindTag,
},
AttributeIndex {
entity_id: u64,
field_name: &'static str,
index: usize,
len: usize,
},
MissingReference {
from: u64,
to: u64,
field_name: &'static str,
},
WrongEntityType {
entity_id: u64,
field_name: &'static str,
expected: &'static str,
actual: String,
},
UnexpectedEntityForm { entity_id: u64, detail: String },
UnsupportedEntity { entity_id: u64, name: String },
DimensionMismatch {
entity_id: u64,
field_name: &'static str,
expected: usize,
actual: usize,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeKindTag {
Integer,
Real,
String,
Enum,
Binary,
EntityRef,
Unset,
Derived,
List,
Typed,
}
impl AttributeKindTag {
#[must_use]
pub fn from_attribute(attr: &Attribute) -> Self {
match attr {
Attribute::Integer(_) => Self::Integer,
Attribute::Real(_) => Self::Real,
Attribute::String(_) => Self::String,
Attribute::Enum(_) => Self::Enum,
Attribute::Binary(_) => Self::Binary,
Attribute::EntityRef(_) => Self::EntityRef,
Attribute::Unset => Self::Unset,
Attribute::Derived => Self::Derived,
Attribute::List(_) => Self::List,
Attribute::Typed { .. } => Self::Typed,
}
}
}
impl std::fmt::Display for AttributeKindTag {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Integer => write!(f, "Integer"),
Self::Real => write!(f, "Real"),
Self::String => write!(f, "String"),
Self::Enum => write!(f, "Enum"),
Self::Binary => write!(f, "Binary"),
Self::EntityRef => write!(f, "EntityRef"),
Self::Unset => write!(f, "Unset"),
Self::Derived => write!(f, "Derived"),
Self::List => write!(f, "List"),
Self::Typed => write!(f, "Typed"),
}
}
}
impl std::fmt::Display for ConvertError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::AttributeCount {
entity_id,
entity_name,
expected,
actual,
} => write!(
f,
"entity #{entity_id} ({entity_name}): \
expected {expected} attributes, got {actual}"
),
Self::AttributeType {
entity_id,
field_name,
expected,
actual,
} => write!(
f,
"entity #{entity_id}: field '{field_name}' \
expected {expected}, found {actual}"
),
Self::AttributeIndex {
entity_id,
field_name,
index,
len,
} => write!(
f,
"entity #{entity_id}: field '{field_name}' \
index {index} out of bounds (len {len})"
),
Self::MissingReference {
from,
to,
field_name,
} => write!(
f,
"entity #{from}: field '{field_name}' \
references #{to} which was not found"
),
Self::WrongEntityType {
entity_id,
field_name,
expected,
actual,
} => write!(
f,
"entity #{entity_id}: field '{field_name}' \
expected {expected}, found {actual}"
),
Self::UnexpectedEntityForm { entity_id, detail } => {
write!(f, "entity #{entity_id}: {detail}")
}
Self::UnsupportedEntity { entity_id, name } => {
write!(f, "entity #{entity_id}: unsupported type {name}")
}
Self::DimensionMismatch {
entity_id,
field_name,
expected,
actual,
} => write!(
f,
"entity #{entity_id}: field '{field_name}' \
expected {expected}D, got {actual}D"
),
}
}
}
impl std::error::Error for ConvertError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn attribute_kind_tag_from_all_variants() {
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Integer(1)),
AttributeKindTag::Integer,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Real(1.0)),
AttributeKindTag::Real,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::String("x".into())),
AttributeKindTag::String,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Enum("T".into())),
AttributeKindTag::Enum,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Binary("FF".into())),
AttributeKindTag::Binary,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::EntityRef(1)),
AttributeKindTag::EntityRef,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Unset),
AttributeKindTag::Unset,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Derived),
AttributeKindTag::Derived,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::List(vec![])),
AttributeKindTag::List,
);
assert_eq!(
AttributeKindTag::from_attribute(&Attribute::Typed {
type_name: "X".into(),
value: Box::new(Attribute::Integer(1)),
}),
AttributeKindTag::Typed,
);
}
#[test]
fn attribute_kind_tag_display() {
assert_eq!(AttributeKindTag::Real.to_string(), "Real");
assert_eq!(AttributeKindTag::EntityRef.to_string(), "EntityRef");
assert_eq!(AttributeKindTag::List.to_string(), "List");
}
#[test]
fn convert_error_display_attribute_type() {
let err = ConvertError::AttributeType {
entity_id: 53,
field_name: "axis",
expected: "EntityRef",
actual: AttributeKindTag::Real,
};
assert_eq!(
err.to_string(),
"entity #53: field 'axis' expected EntityRef, found Real"
);
}
#[test]
fn convert_error_display_missing_reference() {
let err = ConvertError::MissingReference {
from: 10,
to: 99,
field_name: "location",
};
assert_eq!(
err.to_string(),
"entity #10: field 'location' references #99 which was not found"
);
}
#[test]
fn convert_error_implements_std_error() {
fn assert_error<E: std::error::Error>(_: &E) {}
let err = ConvertError::UnsupportedEntity {
entity_id: 1,
name: "UNKNOWN".into(),
};
assert_error(&err);
}
}