1use crate::{Result, UnityAssetError, UnityValue};
7use std::collections::HashMap;
8
9pub trait DynamicAccess {
11 fn get_dynamic(&self, key: &str) -> Option<DynamicValue>;
13
14 fn set_dynamic(&mut self, key: &str, value: DynamicValue) -> Result<()>;
16
17 fn has_dynamic(&self, key: &str) -> bool;
19
20 fn keys_dynamic(&self) -> Vec<String>;
22}
23
24#[derive(Debug, Clone, PartialEq)]
26pub enum DynamicValue {
27 String(String),
29 Integer(i64),
31 Float(f64),
33 Bool(bool),
35 Array(Vec<DynamicValue>),
37 Object(HashMap<String, DynamicValue>),
39 Null,
41}
42
43impl DynamicValue {
44 pub fn from_unity_value(value: &UnityValue) -> Self {
46 match value {
47 UnityValue::String(s) => DynamicValue::String(s.clone()),
48 UnityValue::Integer(i) => DynamicValue::Integer(*i),
49 UnityValue::Float(f) => DynamicValue::Float(*f),
50 UnityValue::Bool(b) => DynamicValue::Bool(*b),
51 UnityValue::Array(arr) => {
52 let converted: Vec<DynamicValue> =
53 arr.iter().map(DynamicValue::from_unity_value).collect();
54 DynamicValue::Array(converted)
55 }
56 UnityValue::Object(obj) => {
57 let converted: HashMap<String, DynamicValue> = obj
58 .iter()
59 .map(|(k, v)| (k.clone(), DynamicValue::from_unity_value(v)))
60 .collect();
61 DynamicValue::Object(converted)
62 }
63 UnityValue::Null => DynamicValue::Null,
64 }
65 }
66
67 pub fn to_unity_value(&self) -> UnityValue {
69 match self {
70 DynamicValue::String(s) => UnityValue::String(s.clone()),
71 DynamicValue::Integer(i) => UnityValue::Integer(*i),
72 DynamicValue::Float(f) => UnityValue::Float(*f),
73 DynamicValue::Bool(b) => UnityValue::Bool(*b),
74 DynamicValue::Array(arr) => {
75 let converted: Vec<UnityValue> =
76 arr.iter().map(DynamicValue::to_unity_value).collect();
77 UnityValue::Array(converted)
78 }
79 DynamicValue::Object(obj) => {
80 let converted: indexmap::IndexMap<String, UnityValue> = obj
81 .iter()
82 .map(|(k, v)| (k.clone(), v.to_unity_value()))
83 .collect();
84 UnityValue::Object(converted)
85 }
86 DynamicValue::Null => UnityValue::Null,
87 }
88 }
89
90 pub fn as_string(&self) -> Option<&str> {
92 match self {
93 DynamicValue::String(s) => Some(s),
94 _ => None,
95 }
96 }
97
98 pub fn as_integer(&self) -> Option<i64> {
100 match self {
101 DynamicValue::Integer(i) => Some(*i),
102 DynamicValue::Float(f) => Some(*f as i64),
103 DynamicValue::Bool(b) => Some(if *b { 1 } else { 0 }),
104 _ => None,
105 }
106 }
107
108 pub fn as_float(&self) -> Option<f64> {
110 match self {
111 DynamicValue::Float(f) => Some(*f),
112 DynamicValue::Integer(i) => Some(*i as f64),
113 _ => None,
114 }
115 }
116
117 pub fn as_bool(&self) -> Option<bool> {
119 match self {
120 DynamicValue::Bool(b) => Some(*b),
121 DynamicValue::Integer(i) => Some(*i != 0),
122 _ => None,
123 }
124 }
125
126 pub fn as_array(&self) -> Option<&Vec<DynamicValue>> {
128 match self {
129 DynamicValue::Array(arr) => Some(arr),
130 _ => None,
131 }
132 }
133
134 pub fn as_array_mut(&mut self) -> Option<&mut Vec<DynamicValue>> {
136 match self {
137 DynamicValue::Array(arr) => Some(arr),
138 _ => None,
139 }
140 }
141
142 pub fn as_object(&self) -> Option<&HashMap<String, DynamicValue>> {
144 match self {
145 DynamicValue::Object(obj) => Some(obj),
146 _ => None,
147 }
148 }
149
150 pub fn as_object_mut(&mut self) -> Option<&mut HashMap<String, DynamicValue>> {
152 match self {
153 DynamicValue::Object(obj) => Some(obj),
154 _ => None,
155 }
156 }
157
158 pub fn is_null(&self) -> bool {
160 matches!(self, DynamicValue::Null)
161 }
162
163 pub fn get_index(&self, index: usize) -> Option<&DynamicValue> {
165 match self {
166 DynamicValue::Array(arr) => arr.get(index),
167 _ => None,
168 }
169 }
170
171 pub fn get_index_mut(&mut self, index: usize) -> Option<&mut DynamicValue> {
173 match self {
174 DynamicValue::Array(arr) => arr.get_mut(index),
175 _ => None,
176 }
177 }
178
179 pub fn get_property(&self, key: &str) -> Option<&DynamicValue> {
181 match self {
182 DynamicValue::Object(obj) => obj.get(key),
183 _ => None,
184 }
185 }
186
187 pub fn get_property_mut(&mut self, key: &str) -> Option<&mut DynamicValue> {
189 match self {
190 DynamicValue::Object(obj) => obj.get_mut(key),
191 _ => None,
192 }
193 }
194
195 pub fn set_property(&mut self, key: String, value: DynamicValue) -> Result<()> {
197 match self {
198 DynamicValue::Object(obj) => {
199 obj.insert(key, value);
200 Ok(())
201 }
202 _ => Err(UnityAssetError::format(
203 "Cannot set property on non-object value",
204 )),
205 }
206 }
207
208 pub fn push(&mut self, value: DynamicValue) -> Result<()> {
210 match self {
211 DynamicValue::Array(arr) => {
212 arr.push(value);
213 Ok(())
214 }
215 _ => Err(UnityAssetError::format("Cannot push to non-array value")),
216 }
217 }
218
219 pub fn concat_string(&mut self, other: &str) -> Result<()> {
221 match self {
222 DynamicValue::String(s) => {
223 s.push_str(other);
224 Ok(())
225 }
226 _ => Err(UnityAssetError::format(
227 "Cannot concatenate to non-string value",
228 )),
229 }
230 }
231
232 pub fn add_numeric(&mut self, other: f64) -> Result<()> {
234 match self {
235 DynamicValue::Integer(i) => {
236 *i += other as i64;
237 Ok(())
238 }
239 DynamicValue::Float(f) => {
240 *f += other;
241 Ok(())
242 }
243 _ => Err(UnityAssetError::format("Cannot add to non-numeric value")),
244 }
245 }
246}
247
248impl std::fmt::Display for DynamicValue {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match self {
251 DynamicValue::String(s) => write!(f, "\"{}\"", s),
252 DynamicValue::Integer(i) => write!(f, "{}", i),
253 DynamicValue::Float(fl) => write!(f, "{}", fl),
254 DynamicValue::Bool(b) => write!(f, "{}", b),
255 DynamicValue::Array(arr) => {
256 write!(f, "[")?;
257 for (i, item) in arr.iter().enumerate() {
258 if i > 0 {
259 write!(f, ", ")?;
260 }
261 write!(f, "{}", item)?;
262 }
263 write!(f, "]")
264 }
265 DynamicValue::Object(obj) => {
266 write!(f, "{{")?;
267 for (i, (key, value)) in obj.iter().enumerate() {
268 if i > 0 {
269 write!(f, ", ")?;
270 }
271 write!(f, "\"{}\": {}", key, value)?;
272 }
273 write!(f, "}}")
274 }
275 DynamicValue::Null => write!(f, "null"),
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn test_dynamic_value_conversion() {
286 let unity_val = UnityValue::String("test".to_string());
287 let dynamic_val = DynamicValue::from_unity_value(&unity_val);
288
289 assert_eq!(dynamic_val, DynamicValue::String("test".to_string()));
290 assert_eq!(dynamic_val.to_unity_value(), unity_val);
291 }
292
293 #[test]
294 fn test_dynamic_value_access() {
295 let mut val = DynamicValue::String("hello".to_string());
296 val.concat_string(" world").unwrap();
297
298 assert_eq!(val.as_string(), Some("hello world"));
299 }
300
301 #[test]
302 fn test_numeric_operations() {
303 let mut val = DynamicValue::Integer(10);
304 val.add_numeric(5.0).unwrap();
305
306 assert_eq!(val.as_integer(), Some(15));
307 }
308}