ability to replace hash map key via reflection + refactoring

This commit is contained in:
Dmitry Stepanov
2026-07-19 21:26:03 +03:00
parent 2d03cc4620
commit d03520e291
3 changed files with 96 additions and 64 deletions
+36 -6
View File
@@ -677,16 +677,25 @@ fn test_hash_map() {
{
let hash_map = hash_map.as_hash_map().unwrap();
assert_eq!(hash_map.reflect_len(), 2);
hash_map.reflect_get(&foo_key, &mut |result| {
assert_eq!(result.unwrap().downcast_ref::<Struct>().unwrap().field, 123)
});
assert_eq!(
hash_map
.reflect_get(&foo_key)
.unwrap()
.downcast_ref::<Struct>()
.unwrap()
.field,
123
);
}
{
let hash_map = hash_map.as_hash_map_mut().unwrap();
hash_map.reflect_get_mut(&bar_key, &mut |result| {
result.unwrap().downcast_mut::<Struct>().unwrap().field = 555
});
hash_map
.reflect_get_mut(&bar_key)
.unwrap()
.downcast_mut::<Struct>()
.unwrap()
.field = 555;
}
// Check path resolution.
@@ -696,6 +705,7 @@ fn test_hash_map() {
hash_map: HashMap<String, Struct>,
}
let mut hash_map2 = hash_map.clone();
let something = Something { hash_map };
something.get_resolve_path::<usize>("hash_map[foo].field", &mut |result| {
@@ -704,4 +714,24 @@ fn test_hash_map() {
something.get_resolve_path::<usize>("hash_map[bar].field", &mut |result| {
assert_eq!(*result.unwrap(), 555)
});
// Check key replacement.
let baz_key = "baz".to_string();
hash_map2
.reflect_replace_key(&bar_key, Box::new(baz_key.clone()))
.unwrap();
{
assert_eq!(hash_map2.reflect_len(), 2);
assert!(hash_map2.reflect_get(&bar_key).is_none());
assert_eq!(
hash_map2
.reflect_get(&baz_key)
.unwrap()
.downcast_ref::<Struct>()
.unwrap()
.field,
555
);
}
}
+37 -29
View File
@@ -30,17 +30,18 @@ pub trait ReflectHashMap: Reflect {
value: Box<dyn Reflect>,
) -> Option<Box<dyn Reflect>>;
fn reflect_len(&self) -> usize;
fn reflect_get(&self, key: &dyn Reflect, func: &mut dyn FnMut(Option<&dyn Reflect>));
fn reflect_get_mut(
&mut self,
key: &dyn Reflect,
func: &mut dyn FnMut(Option<&mut dyn Reflect>),
);
fn reflect_get(&self, key: &dyn Reflect) -> Option<&dyn Reflect>;
fn reflect_get_mut(&mut self, key: &dyn Reflect) -> Option<&mut dyn Reflect>;
fn reflect_get_nth_value_ref(&self, index: usize) -> Option<&dyn Reflect>;
fn reflect_get_nth_value_mut(&mut self, index: usize) -> Option<&mut dyn Reflect>;
fn reflect_get_at(&self, index: usize) -> Option<(&dyn Reflect, &dyn Reflect)>;
fn reflect_get_at_mut(&mut self, index: usize) -> Option<(&dyn Reflect, &mut dyn Reflect)>;
fn reflect_remove(&mut self, key: &dyn Reflect, func: &mut dyn FnMut(Option<Box<dyn Reflect>>));
fn reflect_remove(&mut self, key: &dyn Reflect) -> Option<Box<dyn Reflect>>;
fn reflect_replace_key(
&mut self,
old_key: &dyn Reflect,
new_key: Box<dyn Reflect>,
) -> Result<(), Box<dyn Reflect>>;
}
impl<K, V, S> Reflect for HashMap<K, V, S>
@@ -87,27 +88,23 @@ where
self.len()
}
fn reflect_get(&self, key: &dyn Reflect, func: &mut dyn FnMut(Option<&dyn Reflect>)) {
fn reflect_get(&self, key: &dyn Reflect) -> Option<&dyn Reflect> {
match key.downcast_ref::<K>() {
Some(key) => match self.get(key) {
Some(value) => func(Some(value)),
None => func(None),
Some(value) => Some(value),
None => None,
},
None => func(None),
None => None,
}
}
fn reflect_get_mut(
&mut self,
key: &dyn Reflect,
func: &mut dyn FnMut(Option<&mut dyn Reflect>),
) {
fn reflect_get_mut(&mut self, key: &dyn Reflect) -> Option<&mut dyn Reflect> {
match key.downcast_ref::<K>() {
Some(key) => match self.get_mut(key) {
Some(value) => func(Some(value)),
None => func(None),
Some(value) => Some(value),
None => None,
},
None => func(None),
None => None,
}
}
@@ -131,17 +128,28 @@ where
.map(|(k, v)| (k as &dyn Reflect, v as &mut dyn Reflect))
}
fn reflect_remove(
&mut self,
key: &dyn Reflect,
func: &mut dyn FnMut(Option<Box<dyn Reflect>>),
) {
fn reflect_remove(&mut self, key: &dyn Reflect) -> Option<Box<dyn Reflect>> {
match key.downcast_ref::<K>() {
Some(key) => func(
self.remove(key)
.map(|value| Box::new(value) as Box<dyn Reflect>),
),
None => func(None),
Some(key) => self
.remove(key)
.map(|value| Box::new(value) as Box<dyn Reflect>),
None => None,
}
}
fn reflect_replace_key(
&mut self,
old_key: &dyn Reflect,
new_key: Box<dyn Reflect>,
) -> Result<(), Box<dyn Reflect>> {
if self.reflect_get(&*new_key).is_some() {
return Err(new_key);
}
if let Some(value) = self.reflect_remove(old_key) {
self.reflect_insert(new_key, value);
Ok(())
} else {
Err(new_key)
}
}
}
+23 -29
View File
@@ -828,39 +828,33 @@ unsafe fn make_fake_string_from_slice(string: &str) -> ManuallyDrop<String> {
)))
}
fn try_fetch_by_str_path_ref(
hash_map: &dyn ReflectHashMap,
fn try_fetch_by_str_path_ref<'a>(
hash_map: &'a dyn ReflectHashMap,
path: &str,
func: &mut dyn FnMut(Option<&dyn Reflect>),
) {
// Create fake string here first, this is needed to avoid memory allocations..
) -> Option<&'a dyn Reflect> {
// Create fake string here first, this is needed to avoid memory allocations.
// SAFETY: We won't drop the fake string or mutate it.
let fake_string_key = unsafe { make_fake_string_from_slice(path) };
hash_map.reflect_get(&*fake_string_key, &mut |result| match result {
Some(value) => func(Some(value)),
None => hash_map.reflect_get(&ImmutableString::new(path) as &dyn Reflect, func),
});
match hash_map.reflect_get(&*fake_string_key) {
Some(value) => Some(value),
None => hash_map.reflect_get(&ImmutableString::new(path) as &dyn Reflect),
}
}
fn try_fetch_by_str_path_mut(
hash_map: &mut dyn ReflectHashMap,
fn try_fetch_by_str_path_mut<'a>(
hash_map: &'a mut dyn ReflectHashMap,
path: &str,
func: &mut dyn FnMut(Option<&mut dyn Reflect>),
) {
) -> Option<&'a mut dyn Reflect> {
// Create fake string here first, this is needed to avoid memory allocations..
// SAFETY: We won't drop the fake string or mutate it.
let fake_string_key = unsafe { make_fake_string_from_slice(path) };
let mut succeeded = true;
let hash_map2 = unsafe { &mut *(hash_map as *mut dyn ReflectHashMap) };
hash_map.reflect_get_mut(&*fake_string_key, &mut |result| match result {
Some(value) => func(Some(value)),
None => succeeded = false,
});
if !succeeded {
hash_map.reflect_get_mut(&ImmutableString::new(path) as &dyn Reflect, func)
match hash_map.reflect_get_mut(&*fake_string_key) {
Some(value) => Some(value),
None => hash_map2.reflect_get_mut(&ImmutableString::new(path) as &dyn Reflect),
}
}
@@ -924,9 +918,10 @@ impl<'p> Component<'p> {
Err(_) => func(Err(ReflectPathError::InvalidIndexSyntax { s: path })),
},
None => match reflect.as_hash_map() {
Some(hash_map) => try_fetch_by_str_path_ref(hash_map, path, &mut |result| {
func(result.ok_or(ReflectPathError::NoItemForIndex { s: path }))
}),
Some(hash_map) => func(
try_fetch_by_str_path_ref(hash_map, path)
.ok_or(ReflectPathError::NoItemForIndex { s: path }),
),
None => func(Err(ReflectPathError::NotAnArray)),
},
},
@@ -957,11 +952,10 @@ impl<'p> Component<'p> {
if !succeeded {
match reflect.as_hash_map_mut() {
Some(hash_map) => {
try_fetch_by_str_path_mut(hash_map, path, &mut |result| {
func(result.ok_or(ReflectPathError::NoItemForIndex { s: path }))
})
}
Some(hash_map) => func(
try_fetch_by_str_path_mut(hash_map, path)
.ok_or(ReflectPathError::NoItemForIndex { s: path }),
),
None => func(Err(ReflectPathError::NotAnArray)),
}
}