use super::tags::TagSet;
use serde::{Deserialize, Serialize};
pub type PropertyKey = u32;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PropertyValue {
Bool(bool),
Int(i32),
Float(f32),
Vec2([f32; 2]),
Vec3([f32; 3]),
Vec4([f32; 4]),
Tag(String),
Enum(String),
String(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum PropertyEnum {
Material(super::materials::MaterialClass),
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PropertyAssignment {
pub key: String,
pub value: PropertyValue,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PropertyPredicate {
GreaterThan(String, f32),
LessThan(String, f32),
EqualsBool(String, bool),
EqualsEnum(String, String),
}
#[derive(Debug, Clone, Default)]
pub struct SemanticProperties {
pub traits: TagSet,
pub signals: TagSet,
pub receivers: TagSet,
pub scalars: Vec<(PropertyKey, f32)>,
pub vectors: Vec<(PropertyKey, [f32; 4])>,
pub ints: Vec<(PropertyKey, i32)>,
pub bools: Vec<(PropertyKey, bool)>,
pub enums: Vec<(PropertyKey, PropertyEnum)>,
}
fn vec_insert<V>(entries: &mut Vec<(PropertyKey, V)>, key: PropertyKey, value: V) {
match entries.binary_search_by_key(&key, |(k, _)| *k) {
Ok(idx) => entries[idx].1 = value,
Err(idx) => entries.insert(idx, (key, value)),
}
}
fn vec_get<V: Copy>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<V> {
entries
.binary_search_by_key(&key, |(k, _)| *k)
.ok()
.map(|idx| entries[idx].1)
}
fn vec_get_ref<V>(entries: &[(PropertyKey, V)], key: PropertyKey) -> Option<&V> {
entries
.binary_search_by_key(&key, |(k, _)| *k)
.ok()
.map(|idx| &entries[idx].1)
}
impl SemanticProperties {
pub fn new() -> Self {
Self::default()
}
pub fn set_scalar(&mut self, key: PropertyKey, value: f32) {
vec_insert(&mut self.scalars, key, value);
}
pub fn get_scalar(&self, key: PropertyKey) -> Option<f32> {
vec_get(&self.scalars, key)
}
pub fn set_bool(&mut self, key: PropertyKey, value: bool) {
vec_insert(&mut self.bools, key, value);
}
pub fn get_bool(&self, key: PropertyKey) -> Option<bool> {
vec_get(&self.bools, key)
}
pub fn set_int(&mut self, key: PropertyKey, value: i32) {
vec_insert(&mut self.ints, key, value);
}
pub fn get_int(&self, key: PropertyKey) -> Option<i32> {
vec_get(&self.ints, key)
}
pub fn set_vec4(&mut self, key: PropertyKey, value: [f32; 4]) {
vec_insert(&mut self.vectors, key, value);
}
pub fn get_vec4(&self, key: PropertyKey) -> Option<[f32; 4]> {
vec_get(&self.vectors, key)
}
pub fn get_enum(&self, key: PropertyKey) -> Option<&PropertyEnum> {
vec_get_ref(&self.enums, key)
}
pub fn set_enum(&mut self, key: PropertyKey, value: PropertyEnum) {
vec_insert(&mut self.enums, key, value);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn semantic_properties_scalar_roundtrip() {
let mut props = SemanticProperties::new();
props.set_scalar(0, 42.0);
assert_eq!(props.get_scalar(0), Some(42.0));
assert_eq!(props.get_scalar(1), None);
}
#[test]
fn semantic_properties_bool_roundtrip() {
let mut props = SemanticProperties::new();
props.set_bool(5, true);
assert_eq!(props.get_bool(5), Some(true));
}
#[test]
fn property_value_serde_roundtrip() {
let val = PropertyValue::Float(3.14);
let json = serde_json::to_string(&val).unwrap();
let restored: PropertyValue = serde_json::from_str(&json).unwrap();
assert_eq!(val, restored);
}
#[test]
fn property_assignment_serde() {
let pa = PropertyAssignment {
key: "durability".to_string(),
value: PropertyValue::Float(600.0),
};
let json = serde_json::to_string(&pa).unwrap();
assert!(json.contains("durability"));
}
}