use crate::dynamic_access::{DynamicAccess, DynamicValue};
use crate::error::Result;
use crate::unity_value::UnityValue;
use indexmap::IndexMap;
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub struct UnityClass {
pub class_id: i32,
pub class_name: String,
pub anchor: String,
pub extra_anchor_data: String,
properties: IndexMap<String, UnityValue>,
}
impl UnityClass {
pub fn new(class_id: i32, class_name: String, anchor: String) -> Self {
Self {
class_id,
class_name,
anchor,
extra_anchor_data: String::new(),
properties: IndexMap::new(),
}
}
pub fn get(&self, key: &str) -> Option<&UnityValue> {
self.properties.get(key)
}
pub fn get_mut(&mut self, key: &str) -> Option<&mut UnityValue> {
self.properties.get_mut(key)
}
pub fn set<V: Into<UnityValue>>(&mut self, key: String, value: V) {
self.properties.insert(key, value.into());
}
pub fn has_property(&self, key: &str) -> bool {
self.properties.contains_key(key)
}
pub fn property_names(&self) -> impl Iterator<Item = &String> {
self.properties.keys()
}
pub fn properties(&self) -> &IndexMap<String, UnityValue> {
&self.properties
}
pub fn properties_mut(&mut self) -> &mut IndexMap<String, UnityValue> {
&mut self.properties
}
pub fn update_properties(&mut self, other: IndexMap<String, UnityValue>) {
for (key, value) in other {
self.properties.insert(key, value);
}
}
pub fn serialized_properties(&self) -> IndexMap<String, UnityValue> {
self.properties.clone()
}
pub fn name(&self) -> Option<&str> {
self.get("m_Name").and_then(|v| v.as_str())
}
}
impl fmt::Display for UnityClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}({})", self.class_name, self.class_id)
}
}
impl DynamicAccess for UnityClass {
fn get_dynamic(&self, key: &str) -> Option<DynamicValue> {
self.properties.get(key).map(DynamicValue::from_unity_value)
}
fn set_dynamic(&mut self, key: &str, value: DynamicValue) -> Result<()> {
self.properties
.insert(key.to_string(), value.to_unity_value());
Ok(())
}
fn has_dynamic(&self, key: &str) -> bool {
self.properties.contains_key(key)
}
fn keys_dynamic(&self) -> Vec<String> {
self.properties.keys().cloned().collect()
}
}
#[derive(Debug, Default)]
pub struct UnityClassRegistry {
classes: HashMap<String, fn(i32, String, String) -> UnityClass>,
}
impl UnityClassRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_class<F>(&mut self, class_id: i32, class_name: &str, _constructor: F)
where
F: Fn(i32, String, String) -> UnityClass + 'static,
{
let key = format!("{}-{}", class_id, class_name);
self.classes.insert(key, UnityClass::new);
}
pub fn get_or_create_class(
&self,
class_id: i32,
class_name: &str,
anchor: String,
) -> UnityClass {
let key = format!("{}-{}", class_id, class_name);
if let Some(constructor) = self.classes.get(&key) {
constructor(class_id, class_name.to_string(), anchor)
} else {
UnityClass::new(class_id, class_name.to_string(), anchor)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unity_class_creation() {
let mut class = UnityClass::new(1, "GameObject".to_string(), "123".to_string());
class.set("m_Name".to_string(), "TestObject");
assert_eq!(class.class_name, "GameObject");
assert_eq!(class.name(), Some("TestObject"));
}
#[test]
fn test_unity_class_registry() {
let registry = UnityClassRegistry::new();
let class = registry.get_or_create_class(1, "GameObject", "123".to_string());
assert_eq!(class.class_id, 1);
assert_eq!(class.class_name, "GameObject");
assert_eq!(class.anchor, "123");
}
#[test]
fn test_dynamic_access() {
let mut class = UnityClass::new(1, "GameObject".to_string(), "123".to_string());
let value = DynamicValue::String("TestName".to_string());
class.set_dynamic("m_Name", value).unwrap();
let retrieved = class.get_dynamic("m_Name").unwrap();
assert_eq!(retrieved.as_string(), Some("TestName"));
assert!(class.has_dynamic("m_Name"));
assert!(!class.has_dynamic("nonexistent"));
let keys = class.keys_dynamic();
assert!(keys.contains(&"m_Name".to_string()));
}
}