1use std::collections::BTreeMap;
13
14use super::{DictMap, VmValue};
15
16pub trait DictInsert {
21 fn dict_insert(&mut self, key: String, value: VmValue);
22}
23
24impl DictInsert for BTreeMap<String, VmValue> {
25 fn dict_insert(&mut self, key: String, value: VmValue) {
26 self.insert(key, value);
27 }
28}
29
30impl DictInsert for DictMap {
31 fn dict_insert(&mut self, key: String, value: VmValue) {
32 self.insert(key, value);
33 }
34}
35
36pub trait VmDictExt {
42 fn put(&mut self, key: &str, value: VmValue);
44 fn put_str(&mut self, key: &str, value: impl AsRef<str>);
46 fn put_opt_str(&mut self, key: &str, value: Option<impl AsRef<str>>);
48 fn put_opt(&mut self, key: &str, value: Option<VmValue>);
50 fn put_bool(&mut self, key: &str, value: bool);
52 fn put_int(&mut self, key: &str, value: i64);
54}
55
56pub trait DictRetain {
64 fn retain(&mut self, keep: impl FnMut(&String, &mut VmValue) -> bool);
65}
66
67impl DictRetain for DictMap {
68 fn retain(&mut self, mut keep: impl FnMut(&String, &mut VmValue) -> bool) {
69 let mut result = DictMap::new();
70 for (k, v) in self.iter() {
71 let mut v = v.clone();
72 if keep(k, &mut v) {
73 result.insert(k.clone(), v);
74 }
75 }
76 *self = result;
77 }
78}
79
80impl<M: DictInsert> VmDictExt for M {
81 fn put(&mut self, key: &str, value: VmValue) {
82 self.dict_insert(key.to_string(), value);
83 }
84
85 fn put_str(&mut self, key: &str, value: impl AsRef<str>) {
86 self.dict_insert(key.to_string(), VmValue::string(value));
87 }
88
89 fn put_opt_str(&mut self, key: &str, value: Option<impl AsRef<str>>) {
90 if let Some(value) = value {
91 self.dict_insert(key.to_string(), VmValue::string(value));
92 }
93 }
94
95 fn put_opt(&mut self, key: &str, value: Option<VmValue>) {
96 if let Some(value) = value {
97 self.dict_insert(key.to_string(), value);
98 }
99 }
100
101 fn put_bool(&mut self, key: &str, value: bool) {
102 self.dict_insert(key.to_string(), VmValue::Bool(value));
103 }
104
105 fn put_int(&mut self, key: &str, value: i64) {
106 self.dict_insert(key.to_string(), VmValue::Int(value));
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn put_str_matches_manual_construction() {
116 let mut dict = BTreeMap::new();
117 dict.put_str("k", "v");
118 match dict.get("k") {
119 Some(VmValue::String(s)) => assert_eq!(s.as_ref(), "v"),
120 other => panic!("expected string value, got {other:?}"),
121 }
122 }
123
124 #[test]
125 fn put_opt_str_skips_none_and_inserts_some() {
126 let mut dict = BTreeMap::new();
127 dict.put_opt_str("present", Some("v"));
128 dict.put_opt_str("absent", None::<&str>);
129 assert!(dict.contains_key("present"));
130 assert!(!dict.contains_key("absent"));
131 }
132
133 #[test]
134 fn put_handles_scalars() {
135 let mut dict = BTreeMap::new();
136 dict.put_bool("b", true);
137 dict.put_int("n", 7);
138 dict.put("nil", VmValue::Nil);
139 assert!(matches!(dict.get("b"), Some(VmValue::Bool(true))));
140 assert!(matches!(dict.get("n"), Some(VmValue::Int(7))));
141 assert!(matches!(dict.get("nil"), Some(VmValue::Nil)));
142 }
143}