Skip to main content

arora_types/
keyvalue.rs

1use crate::value::Value;
2use derive_more::Display;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use uuid::Uuid;
6
7use crate::gen_bb_uuid;
8
9/// A collection of named `KeyValueField`s identified by a UUID.
10///
11/// # Construction
12/// Use the provided `From` implementations. Supported conversions:
13/// * `Vec<KeyValueField>` -> `KeyValue`
14/// * `[KeyValueField; N]` (array) -> `KeyValue`
15/// * `KeyValueSet` -> `KeyValue`
16/// * `(Uuid, Vec<KeyValueField>)`, `(Uuid, [KeyValueField; N])`, `(Uuid, KeyValueSet)` and `(Uuid, HashMap<String, KeyValueField>)` -> `KeyValue` with explicit id
17/// * Any `KeyValue` -> `Value` via `into()`
18///
19/// ```rust
20/// use arora_types::keyvalue::{KeyValue, KeyValueField, KeyValueItems};
21/// use arora_types::value::Value;
22/// use arora_types::gen_bb_uuid;
23///
24/// // From a Vec
25/// let kv: KeyValue = vec![
26///   KeyValueField::new("health", Value::I32(100)),
27///   KeyValueField::new("mana", Value::I32(50)),
28/// ].into();
29///
30/// // From an array
31/// let position: KeyValue = [
32///   KeyValueField::new("x", Value::F32(1.0)),
33///   KeyValueField::new("y", Value::F32(2.0)),
34/// ].into();
35///
36/// // Explicit id with a Vec
37/// let id = gen_bb_uuid();
38/// let kv_with_id: KeyValue = (id, vec![
39///   KeyValueField::new("level", Value::I32(5)),
40///   KeyValueField::new("xp", Value::I64(9000)),
41/// ]).into();
42///
43/// // Nested structure via helper
44/// let stats_set = KeyValueItems::from(vec![
45///   KeyValueField::new("strength", Value::I32(50)),
46///   KeyValueField::new("agility", Value::I32(75)),
47/// ]);
48/// let player: KeyValue = vec![
49///   KeyValueField::new("health", Value::I32(100)),
50///   KeyValueField::new_nested_kv("stats", &stats_set),
51/// ].into();
52///
53/// // Convert to Value only when needed
54/// let value: Value = player.clone().into();
55/// assert!(matches!(value, Value::KeyValue(_)));
56/// ```
57#[derive(Debug, Clone, Display, Serialize, Deserialize, PartialEq)]
58#[display("KV({:?})", fields)]
59pub struct KeyValue {
60  pub id: Uuid,
61  pub fields: HashMap<String, KeyValueField>,
62}
63
64impl Default for KeyValue {
65  fn default() -> Self {
66    Self::new()
67  }
68}
69
70impl KeyValue {
71  pub fn new() -> Self {
72    KeyValue::new_with_id(gen_bb_uuid())
73  }
74
75  pub fn new_with_id(id: Uuid) -> Self {
76    Self {
77      id,
78      fields: HashMap::new(),
79    }
80  }
81
82  pub fn set_field(&mut self, field: KeyValueField) {
83    self.fields.insert(field.name.clone(), field);
84  }
85
86  pub fn set_field_value(&mut self, key: &str, value: Value) {
87    let key_str = key.to_string();
88    if let Some(existing_field) = self.fields.get_mut(&key_str) {
89      // Update the value of the existing field
90      existing_field.value = Some(Box::new(value));
91    } else {
92      // Create a new field if it doesn't exist
93      let field = KeyValueField::new(key_str.clone(), value);
94      self.fields.insert(key_str, field);
95    }
96  }
97
98  pub fn get_fields(&self) -> &HashMap<String, KeyValueField> {
99    &self.fields
100  }
101
102  pub fn get_field_keys(&self) -> Vec<String> {
103    self.fields.keys().cloned().collect()
104  }
105
106  pub fn get_field(&self, key: &str) -> Option<&KeyValueField> {
107    self.fields.get(key)
108  }
109
110  pub fn as_value(self) -> Value {
111    Value::KeyValue(self)
112  }
113}
114
115#[derive(Debug, Clone, Display, Serialize, Deserialize, PartialEq)]
116#[display("{} ({}): {:?}", name, id, value)]
117pub struct KeyValueField {
118  pub id: Uuid,
119  pub name: String,
120  pub value: Option<Box<Value>>,
121}
122
123/// Wrapper type representing a collection of `KeyValueField`s. This allows us to
124/// extend functionality (validation, ordering rules, etc.) without changing all
125/// call sites that currently use `Vec<KeyValueField>` or slices.
126#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
127pub struct KeyValueItems(pub Vec<KeyValueField>);
128
129impl std::ops::Deref for KeyValueItems {
130  type Target = [KeyValueField];
131  fn deref(&self) -> &Self::Target {
132    &self.0
133  }
134}
135
136impl IntoIterator for KeyValueItems {
137  type Item = KeyValueField;
138  type IntoIter = std::vec::IntoIter<KeyValueField>;
139  fn into_iter(self) -> Self::IntoIter {
140    self.0.into_iter()
141  }
142}
143
144impl From<Vec<KeyValueField>> for KeyValueItems {
145  fn from(v: Vec<KeyValueField>) -> Self {
146    // Deduplicate preserving the last occurrence, O(n^2) worst case on small n.
147    let mut kvis = KeyValueItems::new();
148    for f in v.into_iter() {
149      kvis.set(f);
150    }
151    kvis
152  }
153}
154
155impl<'a> From<&'a [KeyValueField]> for KeyValueItems {
156  fn from(slice: &'a [KeyValueField]) -> Self {
157    let mut kvis = KeyValueItems::new();
158    for f in slice.iter().cloned() {
159      kvis.set(f);
160    }
161    kvis
162  }
163}
164
165impl From<KeyValueItems> for Vec<KeyValueField> {
166  fn from(kvs: KeyValueItems) -> Self {
167    kvs.0
168  }
169}
170
171impl KeyValueItems {
172  pub fn new() -> Self {
173    Self(Vec::new())
174  }
175  /// Insert a field replacing any existing field with the same name.
176  /// Returns the replaced field if there was one.
177  /// This follows the same behaviour of HashMap.
178  pub fn set(&mut self, field: KeyValueField) -> Option<KeyValueField> {
179    if let Some(idx) = self.0.iter().position(|f| f.name == field.name) {
180      let mut old = field; // we will swap to return the old value
181      std::mem::swap(&mut self.0[idx], &mut old);
182      Some(old)
183    } else {
184      self.0.push(field);
185      None
186    }
187  }
188
189  pub fn is_empty(&self) -> bool {
190    self.0.is_empty()
191  }
192  pub fn len(&self) -> usize {
193    self.0.len()
194  }
195  pub fn into_inner(self) -> Vec<KeyValueField> {
196    self.0
197  }
198  pub fn iter(&self) -> std::slice::Iter<'_, KeyValueField> {
199    self.0.iter()
200  }
201  pub fn get(&self, name: &str) -> Option<&KeyValueField> {
202    self.0.iter().find(|f| f.name == name)
203  }
204
205  pub fn from_hash<K>(pairs: impl IntoIterator<Item = (K, KeyValueField)>) -> KeyValueItems
206  where
207    K: Into<String>,
208  {
209    use std::collections::HashMap; // temporary map to de-duplicate by name
210    let mut map: HashMap<String, KeyValueField> = HashMap::new();
211    for (k, v) in pairs.into_iter() {
212      map.insert(k.into(), v); // last wins
213    }
214    KeyValueItems(map.into_values().collect())
215  }
216}
217
218impl AsRef<[KeyValueField]> for KeyValueItems {
219  fn as_ref(&self) -> &[KeyValueField] {
220    &self.0
221  }
222}
223
224impl KeyValueField {
225  pub fn new<S: Into<String>>(name: S, value: Value) -> Self {
226    Self::new_with_option(name, Some(value))
227  }
228
229  pub fn new_with_option<S: Into<String>>(name: S, value: Option<Value>) -> Self {
230    Self::new_with_id_and_option(name, gen_bb_uuid(), value)
231  }
232
233  pub fn new_with_id<S: Into<String>>(name: S, id: Uuid, value: Value) -> Self {
234    Self {
235      name: name.into(),
236      id,
237      value: Some(Box::new(value)),
238    }
239  }
240
241  pub fn new_with_id_and_option<S: Into<String>>(name: S, id: Uuid, value: Option<Value>) -> Self {
242    Self {
243      name: name.into(),
244      id,
245      value: value.map(Box::new),
246    }
247  }
248
249  pub fn new_nested_kv<S: Into<String>, F: AsRef<[KeyValueField]>>(kv_name: S, fields: &F) -> Self {
250    Self::new_nested_kv_with_kv_id(kv_name, gen_bb_uuid(), fields)
251  }
252
253  pub fn new_nested_kv_with_kv_id<S: Into<String>, F: AsRef<[KeyValueField]>>(
254    kv_name: S,
255    kv_id: Uuid,
256    fields: &F,
257  ) -> Self {
258    Self::new_nested_kv_with_both_ids(kv_name, gen_bb_uuid(), kv_id, fields)
259  }
260
261  pub fn new_nested_kv_with_both_ids<S: Into<String>, F: AsRef<[KeyValueField]>>(
262    kv_name: S,
263    field_id: Uuid,
264    kv_id: Uuid,
265    fields: &F,
266  ) -> Self {
267    // Build KeyValue explicitly then convert to Value
268    let kv: KeyValue = (kv_id, fields.as_ref()).into();
269    KeyValueField::new_with_id(kv_name, field_id, kv.into())
270  }
271}
272
273// ---------------------------------------------------------------------------
274// From conversions for vectors and hashmaps of fields
275// ---------------------------------------------------------------------------
276
277impl From<KeyValue> for Value {
278  fn from(kv: KeyValue) -> Self {
279    kv.as_value()
280  }
281}
282
283impl From<Vec<KeyValueField>> for KeyValue {
284  fn from(fields: Vec<KeyValueField>) -> Self {
285    let id = gen_bb_uuid();
286    (id, fields).into()
287  }
288}
289
290impl From<&[KeyValueField]> for KeyValue {
291  fn from(fields: &[KeyValueField]) -> Self {
292    let id = gen_bb_uuid();
293    (id, fields).into()
294  }
295}
296
297impl<const N: usize> From<[KeyValueField; N]> for KeyValue {
298  fn from(arr: [KeyValueField; N]) -> Self {
299    let id = gen_bb_uuid();
300    (id, arr.into_iter().collect::<Vec<_>>()).into()
301  }
302}
303
304impl From<KeyValueItems> for KeyValue {
305  fn from(set: KeyValueItems) -> Self {
306    let id = gen_bb_uuid();
307    (id, set.0).into()
308  }
309}
310
311impl From<(Uuid, Vec<KeyValueField>)> for KeyValue {
312  fn from((id, fields): (Uuid, Vec<KeyValueField>)) -> Self {
313    let mut map = HashMap::with_capacity(fields.len());
314    for f in fields.into_iter() {
315      map.insert(f.name.clone(), f); // last wins semantics inherently
316    }
317    KeyValue { id, fields: map }
318  }
319}
320
321impl From<(Uuid, &[KeyValueField])> for KeyValue {
322  fn from((id, fields): (Uuid, &[KeyValueField])) -> Self {
323    let mut map = HashMap::with_capacity(fields.len());
324    for f in fields.iter().cloned() {
325      map.insert(f.name.clone(), f);
326    }
327    KeyValue { id, fields: map }
328  }
329}
330
331impl From<(Uuid, KeyValueItems)> for KeyValue {
332  fn from((id, set): (Uuid, KeyValueItems)) -> Self {
333    (id, set.0).into()
334  }
335}
336
337impl From<(Uuid, HashMap<String, KeyValueField>)> for KeyValue {
338  fn from((id, map): (Uuid, HashMap<String, KeyValueField>)) -> Self {
339    KeyValue { id, fields: map }
340  }
341}
342
343impl<const N: usize> From<(Uuid, [KeyValueField; N])> for KeyValue {
344  fn from((id, arr): (Uuid, [KeyValueField; N])) -> Self {
345    (id, arr.into_iter().collect::<Vec<_>>()).into()
346  }
347}
348
349impl From<HashMap<String, KeyValueField>> for KeyValue {
350  fn from(map: HashMap<String, KeyValueField>) -> Self {
351    let id = gen_bb_uuid();
352    (id, map).into()
353  }
354}
355
356// Only retain Value conversion from KeyValue; callers build KeyValue explicitly first.
357
358#[cfg(test)]
359mod tests {
360  use super::*;
361  use crate::value::Value;
362
363  #[test]
364  fn test_keyvalue_new() {
365    let uuid = gen_bb_uuid();
366    let kv = KeyValue::new_with_id(uuid);
367    assert_eq!(kv.id, uuid);
368    assert!(kv.fields.is_empty());
369  }
370
371  #[test]
372  fn test_keyvalue_set_field_value_new() {
373    let mut kv = KeyValue::new();
374    let health_value = Value::I32(100);
375
376    // Directly set a field and value entry into the KV
377
378    kv.set_field_value("health", health_value.clone());
379
380    assert_eq!(kv.fields.len(), 1);
381    assert!(kv.fields.contains_key("health"));
382
383    let field = kv.get_field("health").unwrap();
384    match field.value.as_deref() {
385      Some(Value::I32(value)) => assert_eq!(*value, 100),
386      _ => panic!("Expected I32 value"),
387    }
388  }
389
390  #[test]
391  fn test_keyvalue_set_field_value_update_existing() {
392    let mut kv = KeyValue::new();
393
394    // Set initial value
395    kv.set_field_value("health", Value::I32(100));
396
397    // Update existing field
398    kv.set_field_value("health", Value::I32(50));
399
400    assert_eq!(kv.fields.len(), 1);
401    let field = kv.get_field("health").unwrap();
402    match field.value.as_deref() {
403      Some(Value::I32(value)) => assert_eq!(*value, 50),
404      _ => panic!("Expected I32 value"),
405    }
406  }
407
408  #[test]
409  fn test_keyvalue_set_field() {
410    let mut kv = KeyValue::new();
411    let field = KeyValueField::new("health_id", Value::I32(100));
412
413    // Set an entry into the KV using a prebuilt key-value field
414
415    kv.set_field(field.clone());
416
417    assert_eq!(kv.fields.len(), 1);
418    assert!(kv.fields.contains_key("health_id"));
419    assert_eq!(kv.get_field("health_id"), Some(&field));
420  }
421
422  #[test]
423  fn test_keyvalue_get_field_keys() {
424    let mut kv = KeyValue::new();
425    kv.set_field_value("health", Value::I32(100));
426    kv.set_field_value("mana", Value::I32(50));
427    kv.set_field_value("level", Value::I32(5));
428
429    let keys = kv.get_field_keys();
430    assert_eq!(keys.len(), 3);
431    assert!(keys.contains(&"health".to_string()));
432    assert!(keys.contains(&"mana".to_string()));
433    assert!(keys.contains(&"level".to_string()));
434  }
435
436  #[test]
437  fn test_keyvalue_get_field_nonexistent() {
438    let kv = KeyValue::new();
439    assert_eq!(kv.get_field("nonexistent"), None);
440  }
441
442  #[test]
443  fn test_keyvalue_field_new() {
444    let field = KeyValueField::new("test_id", Value::String("test_value".to_string()));
445    assert_eq!(field.name, "test_id");
446    match field.value.as_deref() {
447      Some(Value::String(value)) => assert_eq!(value, "test_value"),
448      _ => panic!("Expected String value"),
449    }
450  }
451
452  #[test]
453  fn test_keyvalue_field_simple_nested_keyvalue() {
454    let id = gen_bb_uuid();
455    let inner_kv = KeyValue::new_with_id(id);
456    let field = KeyValueField::new("test_id", inner_kv.as_value());
457
458    assert_eq!(field.name, "test_id");
459    match field.value.as_deref() {
460      Some(Value::KeyValue(kv)) => assert_eq!(kv.id, id),
461      _ => panic!("Expected KeyValue variant"),
462    }
463  }
464
465  #[test]
466  fn test_simple_make_kv_from_fields() {
467    let fields = vec![
468      KeyValueField::new("health", Value::I32(100)),
469      KeyValueField::new("mana", Value::I32(50)),
470    ];
471    let id = gen_bb_uuid();
472    let kv: KeyValue = KeyValue::from((id, fields));
473    assert_eq!(kv.id, id);
474    assert_eq!(kv.fields.len(), 2);
475    assert!(kv.fields.contains_key("health"));
476    assert!(kv.fields.contains_key("mana"));
477    match kv.fields.get("health").unwrap().value.as_deref() {
478      Some(Value::I32(value)) => assert_eq!(*value, 100),
479      _ => panic!("Expected I32 value"),
480    }
481
482    match kv.fields.get("mana").unwrap().value.as_deref() {
483      Some(Value::I32(value)) => assert_eq!(*value, 50),
484      _ => panic!("Expected I32 value"),
485    }
486  }
487
488  #[test]
489  fn test_keyvalue_nested_structure_without_ids() {
490    // Create a complex nested structure without caring for the ids, showing a clean and simple example
491    let player: KeyValue = {
492      let fields = [
493        KeyValueField::new("health", Value::I32(100)),
494        KeyValueField::new_nested_kv(
495          "stats",
496          &KeyValueItems::from(vec![
497            KeyValueField::new("strength", Value::I32(50)),
498            KeyValueField::new("agility", Value::I32(75)),
499          ]),
500        ),
501        KeyValueField::new_nested_kv(
502          "position",
503          &KeyValueItems::from(vec![
504            KeyValueField::new("x", Value::F32(10.0)),
505            KeyValueField::new("y", Value::F32(20.0)),
506          ]),
507        ),
508      ];
509      KeyValue::from(fields)
510    };
511
512    // Now expect three top-level fields: health, stats, position
513    assert_eq!(player.fields.len(), 3);
514
515    // health
516    let health_field = player.get_field("health").expect("health field");
517    match health_field.value.as_deref() {
518      Some(Value::I32(100)) => {}
519      other => panic!("Expected I32(100) got {:?}", other),
520    }
521
522    // stats nested kv
523    let stats_field = player.get_field("stats").expect("stats field");
524    match stats_field.value.as_deref() {
525      Some(Value::KeyValue(stats_kv)) => {
526        assert_eq!(stats_kv.fields.len(), 2);
527        // strength
528        match stats_kv.get_field("strength").unwrap().value.as_deref() {
529          Some(Value::I32(50)) => {}
530          other => panic!("Expected strength=50 got {:?}", other),
531        }
532        // agility
533        match stats_kv.get_field("agility").unwrap().value.as_deref() {
534          Some(Value::I32(75)) => {}
535          other => panic!("Expected agility=75 got {:?}", other),
536        }
537      }
538      other => panic!("Expected KeyValue for stats got {:?}", other),
539    }
540
541    // position nested kv
542    let position_field = player.get_field("position").expect("position field");
543    match position_field.value.as_deref() {
544      Some(Value::KeyValue(pos_kv)) => {
545        assert_eq!(pos_kv.fields.len(), 2);
546        match pos_kv.get_field("x").unwrap().value.as_deref() {
547          Some(Value::F32(f)) if (*f - 10.0).abs() < f32::EPSILON => {}
548          other => panic!("Expected x=10.0 got {:?}", other),
549        }
550        match pos_kv.get_field("y").unwrap().value.as_deref() {
551          Some(Value::F32(f)) if (*f - 20.0).abs() < f32::EPSILON => {}
552          other => panic!("Expected y=20.0 got {:?}", other),
553        }
554      }
555      other => panic!("Expected KeyValue for position got {:?}", other),
556    }
557  }
558
559  #[test]
560  fn test_make_kv_from_fields_duplicate_names_last_wins() {
561    let fields = vec![
562      KeyValueField::new("health", Value::I32(100)),
563      KeyValueField::new("health", Value::I32(150)), // duplicate name
564    ];
565    let kv: KeyValue = KeyValue::from(fields);
566    assert_eq!(kv.fields.len(), 1); // last wins
567    let health_field = kv.get_field("health").unwrap();
568    match health_field.value.as_deref() {
569      Some(Value::I32(150)) => {}
570      other => panic!("Expected 150 got {:?}", other),
571    }
572  }
573
574  #[test]
575  fn test_keyvalue_nested_structure_with_ids() {
576    let outer_id = gen_bb_uuid();
577    let health_id = gen_bb_uuid();
578    let inner_field_id = gen_bb_uuid();
579    let inner_kv_id = gen_bb_uuid();
580    let strength_id = gen_bb_uuid();
581    let agility_id = gen_bb_uuid();
582
583    let stats_set = KeyValueItems::from(vec![
584      KeyValueField::new_with_id("strength", strength_id, Value::I32(50)),
585      KeyValueField::new_with_id("agility", agility_id, Value::I32(75)),
586    ]);
587
588    let player = KeyValue::from((
589      outer_id,
590      vec![
591        KeyValueField::new_with_id("health", health_id, Value::I32(100)),
592        KeyValueField::new_nested_kv_with_both_ids(
593          "stats",
594          inner_field_id,
595          inner_kv_id,
596          &stats_set,
597        ),
598      ],
599    ));
600
601    assert_eq!(player.id, outer_id);
602    assert_eq!(player.fields.len(), 2);
603    let health_field = player.get_field("health").unwrap();
604    assert_eq!(health_field.id, health_id);
605    match health_field.value.as_ref().unwrap().as_ref() {
606      Value::I32(100) => {}
607      _ => panic!("Expected I32(100)"),
608    }
609    let stats_field = player.get_field("stats").unwrap();
610    assert_eq!(stats_field.id, inner_field_id);
611    match stats_field.value.as_deref() {
612      Some(Value::KeyValue(stats_kv)) => {
613        assert_eq!(stats_kv.id, inner_kv_id);
614        assert_eq!(stats_kv.fields.len(), 2);
615        let strength_field = stats_kv.get_field("strength").unwrap();
616        assert_eq!(strength_field.id, strength_id);
617        match strength_field.value.as_deref() {
618          Some(Value::I32(50)) => {}
619          other => panic!("Expected I32(50) got {:?}", other),
620        }
621        let agility_field = stats_kv.get_field("agility").unwrap();
622        assert_eq!(agility_field.id, agility_id);
623        match agility_field.value.as_deref() {
624          Some(Value::I32(75)) => {}
625          other => panic!("Expected I32(75) got {:?}", other),
626        }
627      }
628      other => panic!("Expected KeyValue for stats got {:?}", other),
629    }
630  }
631
632  #[test]
633  fn test_keyvalue_display() {
634    let mut kv = KeyValue::new();
635    kv.set_field_value("key1", Value::String("value1".to_string()));
636    kv.set_field_value("key2", Value::I32(42));
637
638    let display_str = format!("{}", kv);
639    assert!(display_str.contains("KV("));
640    assert!(display_str.contains("key1"));
641    assert!(display_str.contains("key2"));
642  }
643
644  #[test]
645  fn test_keyvalue_field_display() {
646    let field = KeyValueField::new("test_field", Value::String("test_value".to_string()));
647    let display_str = format!("{}", field);
648    assert!(display_str.contains("test_field"));
649    assert!(display_str.contains("test_value"));
650  }
651
652  #[test]
653  fn test_keyvalue_clone_and_equality() {
654    let mut original = KeyValue::new();
655    original.set_field_value("health", Value::I32(100));
656    original.set_field_value("level", Value::I32(5));
657
658    let cloned = original.clone();
659
660    assert_eq!(original, cloned);
661    assert_eq!(original.id, cloned.id);
662    assert_eq!(original.fields.len(), cloned.fields.len());
663
664    // Verify they're actually separate instances
665    let mut modified = cloned;
666    modified.set_field_value("health", Value::I32(200));
667
668    assert_ne!(original, modified);
669  }
670
671  #[test]
672  fn test_keyvalue_serialization() {
673    use json5;
674
675    let id = gen_bb_uuid();
676    let mut kv = KeyValue::new_with_id(id);
677    kv.set_field_value("health", Value::I32(100));
678    kv.set_field_value("name", Value::String("Hero".to_string()));
679
680    // Test serialization
681    let json = json5::to_string(&kv).expect("Serialization should succeed");
682    assert!(json.contains(&id.to_string()));
683    assert!(json.contains("health"));
684    assert!(json.contains("name"));
685
686    // Test deserialization
687    let deserialized: KeyValue = json5::from_str(&json).expect("Deserialization should succeed");
688    assert_eq!(kv, deserialized);
689  }
690
691  #[test]
692  fn test_keyvalue_empty_operations() {
693    let kv = KeyValue::new();
694
695    assert!(kv.get_field_keys().is_empty());
696    assert!(kv.get_fields().is_empty());
697    assert_eq!(kv.get_field("any_key"), None);
698  }
699
700  #[test]
701  fn test_valueblock_with_different_value_types() {
702    // Test with different primitive types
703    let test_cases = vec![
704      ("bool", Value::Boolean(true)),
705      ("u8", Value::U8(255)),
706      ("u16", Value::U16(65535)),
707      ("u32", Value::U32(4294967295)),
708      ("u64", Value::U64(18446744073709551615)),
709      ("i8", Value::I8(-128)),
710      ("i16", Value::I16(-32768)),
711      ("i32", Value::I32(-2147483648)),
712      ("i64", Value::I64(-9223372036854775808)),
713      ("f32", Value::F32(std::f32::consts::PI)),
714      ("f64", Value::F64(std::f64::consts::E)),
715      ("string", Value::String("test string".to_string())),
716      ("unit", Value::Unit),
717    ];
718
719    for (name, value) in test_cases {
720      let mut kv = KeyValue::new();
721      kv.set_field_value(name, value.clone());
722
723      let retrieved_field = kv.get_field(name).unwrap();
724
725      assert_eq!(
726        retrieved_field.value.as_deref(),
727        Some(&value),
728        "Failed for type: {}",
729        name
730      );
731    }
732  }
733
734  #[test]
735  fn test_keyvalue_field_map_from_hash() {
736    use std::collections::HashMap;
737    // Build an explicit HashMap of KeyValueField entries
738    let mut map: HashMap<String, KeyValueField> = HashMap::new();
739    map.insert("a".into(), KeyValueField::new("a", Value::I32(1)));
740    map.insert("b".into(), KeyValueField::new("b", Value::I32(2)));
741    map.insert("a".into(), KeyValueField::new("a", Value::I32(10))); // overwrite a
742
743    // Auto id conversion from HashMap
744    let auto_kv: KeyValue = map.clone().into();
745    assert_eq!(auto_kv.fields.len(), 2);
746    match auto_kv.get_field("a").unwrap().value.as_deref() {
747      Some(Value::I32(10)) => {}
748      other => panic!("expected 10 got {:?}", other),
749    }
750    match auto_kv.get_field("b").unwrap().value.as_deref() {
751      Some(Value::I32(2)) => {}
752      other => panic!("expected 2 got {:?}", other),
753    }
754
755    // Explicit id conversion
756    let explicit_id = gen_bb_uuid();
757    let kv_with_id: KeyValue = (explicit_id, map).into();
758    assert_eq!(kv_with_id.id, explicit_id);
759    assert_eq!(kv_with_id.fields.len(), 2);
760    assert!(kv_with_id.get_field("a").is_some());
761    assert!(kv_with_id.get_field("b").is_some());
762  }
763
764  // --------------------------- KeyValueSet uniqueness tests ---------------------------
765  #[test]
766  fn test_keyvalueset_from_vec_deduplicates_last_wins() {
767    let set = KeyValueItems::from(vec![
768      KeyValueField::new("health", Value::I32(100)),
769      KeyValueField::new("mana", Value::I32(50)),
770      KeyValueField::new("health", Value::I32(150)), // duplicate later entry
771    ]);
772    assert_eq!(set.len(), 2);
773    let health = set.get("health").unwrap();
774    match health.value.as_deref() {
775      Some(Value::I32(150)) => {}
776      other => panic!("expected 150 got {:?}", other),
777    }
778  }
779
780  #[test]
781  fn test_keyvalueitems_set_replaces() {
782    let mut kvis = KeyValueItems::new();
783    let inserted = kvis.set(KeyValueField::new("speed", Value::I32(10)));
784    assert!(inserted.is_none());
785    let inserted2 = kvis.set(KeyValueField::new("speed", Value::I32(20))); // replace
786    assert!(inserted2.is_some());
787    assert_eq!(kvis.len(), 1);
788    match kvis.get("speed").unwrap().value.as_deref() {
789      Some(Value::I32(20)) => {}
790      other => panic!("expected 20 got {:?}", other),
791    }
792  }
793
794  #[test]
795  fn test_keyvalueitems_set_returns_old() {
796    let mut kvis = KeyValueItems::new();
797    assert!(kvis.set(KeyValueField::new("x", Value::I32(1))).is_none());
798    let old = kvis
799      .set(KeyValueField::new("x", Value::I32(2)))
800      .expect("old value");
801    match old.value.as_deref() {
802      Some(Value::I32(1)) => {}
803      other => panic!("expected old=1 got {:?}", other),
804    }
805    match kvis.get("x").unwrap().value.as_deref() {
806      Some(Value::I32(2)) => {}
807      other => panic!("expected new=2 got {:?}", other),
808    }
809  }
810}