unity_asset_core/
unity_class.rs1use crate::dynamic_access::{DynamicAccess, DynamicValue};
7use crate::error::Result;
8use crate::unity_value::UnityValue;
9use indexmap::IndexMap;
10use std::collections::HashMap;
11use std::fmt;
12
13#[derive(Debug, Clone)]
15pub struct UnityClass {
16 pub class_id: i32,
18 pub class_name: String,
20 pub anchor: String,
22 pub extra_anchor_data: String,
24 properties: IndexMap<String, UnityValue>,
26}
27
28impl UnityClass {
29 pub fn new(class_id: i32, class_name: String, anchor: String) -> Self {
31 Self {
32 class_id,
33 class_name,
34 anchor,
35 extra_anchor_data: String::new(),
36 properties: IndexMap::new(),
37 }
38 }
39
40 pub fn get(&self, key: &str) -> Option<&UnityValue> {
42 self.properties.get(key)
43 }
44
45 pub fn get_mut(&mut self, key: &str) -> Option<&mut UnityValue> {
47 self.properties.get_mut(key)
48 }
49
50 pub fn set<V: Into<UnityValue>>(&mut self, key: String, value: V) {
52 self.properties.insert(key, value.into());
53 }
54
55 pub fn has_property(&self, key: &str) -> bool {
57 self.properties.contains_key(key)
58 }
59
60 pub fn property_names(&self) -> impl Iterator<Item = &String> {
62 self.properties.keys()
63 }
64
65 pub fn properties(&self) -> &IndexMap<String, UnityValue> {
67 &self.properties
68 }
69
70 pub fn properties_mut(&mut self) -> &mut IndexMap<String, UnityValue> {
72 &mut self.properties
73 }
74
75 pub fn update_properties(&mut self, other: IndexMap<String, UnityValue>) {
77 for (key, value) in other {
78 self.properties.insert(key, value);
79 }
80 }
81
82 pub fn serialized_properties(&self) -> IndexMap<String, UnityValue> {
84 self.properties.clone()
85 }
86
87 pub fn name(&self) -> Option<&str> {
89 self.get("m_Name").and_then(|v| v.as_str())
90 }
91}
92
93impl fmt::Display for UnityClass {
94 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 write!(f, "{}({})", self.class_name, self.class_id)
96 }
97}
98
99impl DynamicAccess for UnityClass {
101 fn get_dynamic(&self, key: &str) -> Option<DynamicValue> {
102 self.properties.get(key).map(DynamicValue::from_unity_value)
103 }
104
105 fn set_dynamic(&mut self, key: &str, value: DynamicValue) -> Result<()> {
106 self.properties
107 .insert(key.to_string(), value.to_unity_value());
108 Ok(())
109 }
110
111 fn has_dynamic(&self, key: &str) -> bool {
112 self.properties.contains_key(key)
113 }
114
115 fn keys_dynamic(&self) -> Vec<String> {
116 self.properties.keys().cloned().collect()
117 }
118}
119
120#[derive(Debug, Default)]
122pub struct UnityClassRegistry {
123 classes: HashMap<String, fn(i32, String, String) -> UnityClass>,
125}
126
127impl UnityClassRegistry {
128 pub fn new() -> Self {
130 Self::default()
131 }
132
133 pub fn register_class<F>(&mut self, class_id: i32, class_name: &str, _constructor: F)
135 where
136 F: Fn(i32, String, String) -> UnityClass + 'static,
137 {
138 let key = format!("{}-{}", class_id, class_name);
139 self.classes.insert(key, UnityClass::new);
141 }
142
143 pub fn get_or_create_class(
145 &self,
146 class_id: i32,
147 class_name: &str,
148 anchor: String,
149 ) -> UnityClass {
150 let key = format!("{}-{}", class_id, class_name);
151
152 if let Some(constructor) = self.classes.get(&key) {
153 constructor(class_id, class_name.to_string(), anchor)
154 } else {
155 UnityClass::new(class_id, class_name.to_string(), anchor)
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_unity_class_creation() {
167 let mut class = UnityClass::new(1, "GameObject".to_string(), "123".to_string());
168 class.set("m_Name".to_string(), "TestObject");
169
170 assert_eq!(class.class_name, "GameObject");
171 assert_eq!(class.name(), Some("TestObject"));
172 }
173
174 #[test]
175 fn test_unity_class_registry() {
176 let registry = UnityClassRegistry::new();
177 let class = registry.get_or_create_class(1, "GameObject", "123".to_string());
178
179 assert_eq!(class.class_id, 1);
180 assert_eq!(class.class_name, "GameObject");
181 assert_eq!(class.anchor, "123");
182 }
183
184 #[test]
185 fn test_dynamic_access() {
186 let mut class = UnityClass::new(1, "GameObject".to_string(), "123".to_string());
187
188 let value = DynamicValue::String("TestName".to_string());
190 class.set_dynamic("m_Name", value).unwrap();
191
192 let retrieved = class.get_dynamic("m_Name").unwrap();
193 assert_eq!(retrieved.as_string(), Some("TestName"));
194
195 assert!(class.has_dynamic("m_Name"));
197 assert!(!class.has_dynamic("nonexistent"));
198
199 let keys = class.keys_dynamic();
201 assert!(keys.contains(&"m_Name".to_string()));
202 }
203}