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