1use crate::PropertyIndex;
4
5#[derive(Clone, Debug, PartialEq)]
11pub struct EntityManifest {
12 pub property_names: Vec<String>,
14 pub property_defaults: Vec<f32>,
16 pub alive_property: PropertyIndex,
18}
19
20impl EntityManifest {
21 #[must_use]
23 pub fn property_count(&self) -> usize {
24 self.property_defaults.len()
25 }
26
27 pub fn validate(&self) -> Result<(), EntityManifestError> {
29 if self.property_names.len() != self.property_defaults.len() {
30 return Err(EntityManifestError::PropertyLengthMismatch {
31 names: self.property_names.len(),
32 defaults: self.property_defaults.len(),
33 });
34 }
35 if self.property_defaults.is_empty() {
36 return Err(EntityManifestError::EmptyProperties);
37 }
38 if self.alive_property.0 as usize >= self.property_defaults.len() {
39 return Err(EntityManifestError::AlivePropertyOutOfRange {
40 alive_property: self.alive_property,
41 property_count: self.property_defaults.len(),
42 });
43 }
44 Ok(())
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum EntityManifestError {
51 PropertyLengthMismatch {
53 names: usize,
55 defaults: usize,
57 },
58 EmptyProperties,
60 AlivePropertyOutOfRange {
62 alive_property: PropertyIndex,
64 property_count: usize,
66 },
67}
68
69impl std::fmt::Display for EntityManifestError {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 match self {
72 Self::PropertyLengthMismatch { names, defaults } => write!(
73 f,
74 "entity property names/defaults length mismatch: {names} names, {defaults} defaults"
75 ),
76 Self::EmptyProperties => {
77 write!(f, "entity manifest must declare at least one property")
78 }
79 Self::AlivePropertyOutOfRange {
80 alive_property,
81 property_count,
82 } => write!(
83 f,
84 "alive property {alive_property} is out of range for {property_count} properties"
85 ),
86 }
87 }
88}
89
90impl std::error::Error for EntityManifestError {}
91
92#[cfg(test)]
93mod tests {
94 use crate::{EntityManifest, PropertyIndex};
95
96 #[test]
97 fn property_count_matches_defaults() {
98 let manifest = EntityManifest {
99 property_names: vec!["alive".into(), "hp".into()],
100 property_defaults: vec![1.0, 100.0],
101 alive_property: PropertyIndex(0),
102 };
103
104 assert_eq!(manifest.property_count(), 2);
105 assert_eq!(manifest.validate(), Ok(()));
106 }
107
108 #[test]
109 fn validate_rejects_length_mismatch() {
110 let manifest = EntityManifest {
111 property_names: vec!["alive".into()],
112 property_defaults: vec![1.0, 100.0],
113 alive_property: PropertyIndex(0),
114 };
115
116 assert!(manifest.validate().is_err());
117 }
118
119 #[test]
120 fn validate_rejects_alive_property_out_of_range() {
121 let manifest = EntityManifest {
122 property_names: vec!["alive".into()],
123 property_defaults: vec![1.0],
124 alive_property: PropertyIndex(2),
125 };
126
127 assert!(manifest.validate().is_err());
128 }
129}