Skip to content

Commit 18f3a7b

Browse files
bors[bot]lilizoeyBromeon
authored
Merge #99
99: Add iteration methods to Dictionary r=Bromeon a=sayaks add 2 methods to dictionary: - `iter_shared` for iterating over `(Variant,Variant)` key-values - `keys_shared` for iterating over `Variant` keys in addition iterators have: - `.typed()` to convert an untyped iterator into a typed iterator Co-authored-by: Lili Zoey <[email protected]> Co-authored-by: Jan Haller <[email protected]>
2 parents e594365 + 46fe0cd commit 18f3a7b

File tree

2 files changed

+512
-97
lines changed

2 files changed

+512
-97
lines changed

godot-core/src/builtin/dictionary.rs

+232-40
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@
66

77
use godot_ffi as sys;
88

9-
use crate::builtin::{inner, FromVariant, ToVariant, Variant, VariantConversionError};
9+
use crate::builtin::{inner, FromVariant, ToVariant, Variant};
1010
use crate::obj::Share;
11-
use std::collections::{HashMap, HashSet};
1211
use std::fmt;
13-
use sys::types::*;
12+
use std::marker::PhantomData;
13+
use std::ptr::addr_of_mut;
14+
use sys::types::OpaqueDictionary;
1415
use sys::{ffi_methods, interface_fn, GodotFfi};
1516

1617
use super::Array;
@@ -134,12 +135,12 @@ impl Dictionary {
134135
}
135136

136137
/// Creates a new `Array` containing all the keys currently in the dictionary.
137-
pub fn keys(&self) -> Array {
138+
pub fn keys_array(&self) -> Array {
138139
self.as_inner().keys()
139140
}
140141

141142
/// Creates a new `Array` containing all the values currently in the dictionary.
142-
pub fn values(&self) -> Array {
143+
pub fn values_array(&self) -> Array {
143144
self.as_inner().values()
144145
}
145146

@@ -195,6 +196,28 @@ impl Dictionary {
195196
}
196197
}
197198

199+
/// Returns an iterator over the key-value pairs of the `Dictionary`. The pairs are each of type `(Variant, Variant)`.
200+
/// Each pair references the original `Dictionary`, but instead of a `&`-reference to key-value pairs as
201+
/// you might expect, the iterator returns a (cheap, shallow) copy of each key-value pair.
202+
///
203+
/// Note that it's possible to modify the `Dictionary` through another reference while iterating
204+
/// over it. This will not result in unsoundness or crashes, but will cause the iterator to
205+
/// behave in an unspecified way.
206+
pub fn iter_shared(&self) -> Iter<'_> {
207+
Iter::new(self)
208+
}
209+
210+
/// Returns an iterator over the keys `Dictionary`. The keys are each of type `Variant`. Each key references
211+
/// the original `Dictionary`, but instead of a `&`-reference to keys pairs as you might expect, the
212+
/// iterator returns a (cheap, shallow) copy of each key pair.
213+
///
214+
/// Note that it's possible to modify the `Dictionary` through another reference while iterating
215+
/// over it. This will not result in unsoundness or crashes, but will cause the iterator to
216+
/// behave in an unspecified way.
217+
pub fn keys_shared(&self) -> Keys<'_> {
218+
Keys::new(self)
219+
}
220+
198221
#[doc(hidden)]
199222
pub fn as_inner(&self) -> inner::InnerDictionary {
200223
inner::InnerDictionary::from_outer(self)
@@ -217,41 +240,6 @@ where
217240
}
218241
}
219242

220-
/// Convert this dictionary to a strongly typed rust `HashMap`. If the conversion
221-
/// fails for any key or value, an error is returned.
222-
///
223-
/// Will be replaced by a proper iteration implementation.
224-
impl<K: FromVariant + Eq + std::hash::Hash, V: FromVariant> TryFrom<&Dictionary> for HashMap<K, V> {
225-
type Error = VariantConversionError;
226-
227-
fn try_from(dictionary: &Dictionary) -> Result<Self, Self::Error> {
228-
// TODO: try to panic or something if modified while iterating
229-
// Though probably better to fix when implementing iteration proper
230-
dictionary
231-
.keys()
232-
.iter_shared()
233-
.zip(dictionary.values().iter_shared())
234-
.map(|(key, value)| Ok((K::try_from_variant(&key)?, V::try_from_variant(&value)?)))
235-
.collect()
236-
}
237-
}
238-
239-
/// Convert the keys of this dictionary to a strongly typed rust `HashSet`. If the
240-
/// conversion fails for any key, an error is returned.
241-
impl<K: FromVariant + Eq + std::hash::Hash> TryFrom<&Dictionary> for HashSet<K> {
242-
type Error = VariantConversionError;
243-
244-
fn try_from(dictionary: &Dictionary) -> Result<Self, Self::Error> {
245-
// TODO: try to panic or something if modified while iterating
246-
// Though probably better to fix when implementing iteration proper
247-
dictionary
248-
.keys()
249-
.iter_shared()
250-
.map(|key| K::try_from_variant(&key))
251-
.collect()
252-
}
253-
}
254-
255243
/// Inserts all key-values from the iterator into the dictionary,
256244
/// replacing values with existing keys with new values returned
257245
/// from the iterator.
@@ -320,6 +308,210 @@ impl Share for Dictionary {
320308
}
321309
}
322310

311+
struct DictionaryIter<'a> {
312+
last_key: Option<Variant>,
313+
dictionary: &'a Dictionary,
314+
is_first: bool,
315+
}
316+
317+
impl<'a> DictionaryIter<'a> {
318+
fn new(dictionary: &'a Dictionary) -> Self {
319+
Self {
320+
last_key: None,
321+
dictionary,
322+
is_first: true,
323+
}
324+
}
325+
326+
fn call_init(dictionary: &Dictionary) -> Option<Variant> {
327+
// SAFETY:
328+
// `dictionary` is a valid `Dictionary` since we have a reference to it,
329+
// so this will call the implementation for dictionaries.
330+
// `variant` is an initialized and valid `Variant`.
331+
let variant: Variant = Variant::nil();
332+
unsafe { Self::call_iter_fn(interface_fn!(variant_iter_init), dictionary, variant) }
333+
}
334+
335+
fn call_next(dictionary: &Dictionary, last_key: Variant) -> Option<Variant> {
336+
// SAFETY:
337+
// `dictionary` is a valid `Dictionary` since we have a reference to it,
338+
// so this will call the implementation for dictionaries.
339+
// `last_key` is an initialized and valid `Variant`, since we own a copy of it.
340+
unsafe { Self::call_iter_fn(interface_fn!(variant_iter_next), dictionary, last_key) }
341+
}
342+
343+
/// # SAFETY:
344+
/// `iter_fn` must point to a valid function that interprets the parameters according to their type specification.
345+
unsafe fn call_iter_fn(
346+
iter_fn: unsafe extern "C" fn(
347+
sys::GDExtensionConstVariantPtr,
348+
sys::GDExtensionVariantPtr,
349+
*mut sys::GDExtensionBool,
350+
) -> sys::GDExtensionBool,
351+
dictionary: &Dictionary,
352+
next_var: Variant,
353+
) -> Option<Variant> {
354+
let dictionary = dictionary.to_variant();
355+
let mut valid: u8 = 0;
356+
357+
let has_next = iter_fn(
358+
dictionary.var_sys(),
359+
next_var.var_sys(),
360+
addr_of_mut!(valid),
361+
);
362+
let valid = u8_to_bool(valid);
363+
let has_next = u8_to_bool(has_next);
364+
365+
if has_next {
366+
assert!(valid);
367+
Some(next_var)
368+
} else {
369+
None
370+
}
371+
}
372+
373+
fn next_key(&mut self) -> Option<Variant> {
374+
let new_key = if self.is_first {
375+
self.is_first = false;
376+
Self::call_init(self.dictionary)
377+
} else {
378+
Self::call_next(self.dictionary, self.last_key.take()?)
379+
};
380+
self.last_key = new_key.clone();
381+
new_key
382+
}
383+
384+
fn next_key_value(&mut self) -> Option<(Variant, Variant)> {
385+
let key = self.next_key()?;
386+
if !self.dictionary.contains_key(key.clone()) {
387+
return None;
388+
}
389+
390+
let value = self.dictionary.as_inner().get(key.clone(), Variant::nil());
391+
Some((key, value))
392+
}
393+
}
394+
395+
/// An iterator over key-value pairs from a `Dictionary`.
396+
///
397+
/// See [Dictionary::iter_shared()] for more information about iteration over dictionaries.
398+
pub struct Iter<'a> {
399+
iter: DictionaryIter<'a>,
400+
}
401+
402+
impl<'a> Iter<'a> {
403+
fn new(dictionary: &'a Dictionary) -> Self {
404+
Self {
405+
iter: DictionaryIter::new(dictionary),
406+
}
407+
}
408+
409+
/// Creates an iterator that will convert each `(Variant, Variant)` key-value pair into
410+
/// a `(K,V)` key-value pair, panicking upon failure to convert.
411+
pub fn typed<K: FromVariant, V: FromVariant>(self) -> TypedIter<'a, K, V> {
412+
TypedIter::from_untyped(self)
413+
}
414+
}
415+
416+
impl<'a> Iterator for Iter<'a> {
417+
type Item = (Variant, Variant);
418+
419+
fn next(&mut self) -> Option<Self::Item> {
420+
self.iter.next_key_value()
421+
}
422+
}
423+
424+
/// An iterator over keys from a `Dictionary`.
425+
///
426+
/// See [Dictionary::keys_shared()] for more information about iteration over dictionaries.
427+
pub struct Keys<'a> {
428+
iter: DictionaryIter<'a>,
429+
}
430+
431+
impl<'a> Keys<'a> {
432+
fn new(dictionary: &'a Dictionary) -> Self {
433+
Self {
434+
iter: DictionaryIter::new(dictionary),
435+
}
436+
}
437+
438+
/// Creates an iterator that will convert each `Variant` key into a key of type `K`,
439+
/// panicking upon failure to convert.
440+
pub fn typed<K: FromVariant>(self) -> TypedKeys<'a, K> {
441+
TypedKeys::from_untyped(self)
442+
}
443+
}
444+
impl<'a> Iterator for Keys<'a> {
445+
type Item = Variant;
446+
447+
fn next(&mut self) -> Option<Self::Item> {
448+
self.iter.next_key()
449+
}
450+
}
451+
452+
/// An iterator over key-value pairs from a `Dictionary` that will attempt to convert each
453+
/// key-value pair into a `(K,V)`.
454+
///
455+
/// See [Dictionary::iter_shared()] for more information about iteration over dictionaries.
456+
pub struct TypedIter<'a, K, V> {
457+
iter: DictionaryIter<'a>,
458+
_k: PhantomData<K>,
459+
_v: PhantomData<V>,
460+
}
461+
462+
impl<'a, K, V> TypedIter<'a, K, V> {
463+
fn from_untyped(value: Iter<'a>) -> Self {
464+
Self {
465+
iter: value.iter,
466+
_k: PhantomData,
467+
_v: PhantomData,
468+
}
469+
}
470+
}
471+
472+
impl<'a, K: FromVariant, V: FromVariant> Iterator for TypedIter<'a, K, V> {
473+
type Item = (K, V);
474+
475+
fn next(&mut self) -> Option<Self::Item> {
476+
self.iter
477+
.next_key_value()
478+
.map(|(key, value)| (K::from_variant(&key), V::from_variant(&value)))
479+
}
480+
}
481+
482+
/// An iterator over keys from a `Dictionary` that will attempt to convert each key into a `K`.
483+
///
484+
/// See [Dictionary::iter_shared()] for more information about iteration over dictionaries.
485+
pub struct TypedKeys<'a, K> {
486+
iter: DictionaryIter<'a>,
487+
_k: PhantomData<K>,
488+
}
489+
490+
impl<'a, K> TypedKeys<'a, K> {
491+
fn from_untyped(value: Keys<'a>) -> Self {
492+
Self {
493+
iter: value.iter,
494+
_k: PhantomData,
495+
}
496+
}
497+
}
498+
499+
impl<'a, K: FromVariant> Iterator for TypedKeys<'a, K> {
500+
type Item = K;
501+
502+
fn next(&mut self) -> Option<Self::Item> {
503+
self.iter.next_key().map(|k| K::from_variant(&k))
504+
}
505+
}
506+
507+
fn u8_to_bool(u: u8) -> bool {
508+
match u {
509+
0 => false,
510+
1 => true,
511+
_ => panic!("Invalid boolean value {u}"),
512+
}
513+
}
514+
323515
/// Creates a new dictionary with the given keys and values, the syntax mirrors
324516
/// Godot's dictionary creation syntax.
325517
///

0 commit comments

Comments
 (0)