Skip to content

Commit 50c4b2e

Browse files
authored
Try #92:
2 parents 4ebdc5e + 3786a0a commit 50c4b2e

File tree

9 files changed

+853
-2
lines changed

9 files changed

+853
-2
lines changed

godot-core/src/builtin/arrays.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ impl_builtin_traits! {
559559
for Array {
560560
Default => array_construct_default;
561561
Drop => array_destroy;
562+
PartialEq => array_operator_equal;
562563
}
563564
}
564565

godot-core/src/builtin/dictionary.rs

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
use godot_ffi as sys;
2+
3+
use crate::builtin::{inner, FromVariant, ToVariant, Variant, VariantConversionError};
4+
use crate::obj::Share;
5+
use std::collections::{HashMap, HashSet};
6+
use std::fmt;
7+
use sys::types::*;
8+
use sys::{ffi_methods, interface_fn, GodotFfi};
9+
10+
use super::Array;
11+
12+
/// Godot's `Dictionary` type.
13+
///
14+
/// The keys and values of the array are all `Variant`, so they can all be of different types.
15+
///
16+
/// Keys are assumed to be cheap to clone, this will usually be the case especially for
17+
/// value types and reference counted types.
18+
///
19+
/// # Thread safety
20+
///
21+
/// The same principles apply as for [`Array`]. Consult its documentation for details.
22+
#[repr(C)]
23+
pub struct Dictionary {
24+
opaque: OpaqueDictionary,
25+
}
26+
27+
impl Dictionary {
28+
fn from_opaque(opaque: OpaqueDictionary) -> Self {
29+
Self { opaque }
30+
}
31+
32+
/// Constructs an empty `Dictionary`.
33+
pub fn new() -> Self {
34+
Self::default()
35+
}
36+
37+
/// Removes all key-value pairs from the dictionary. Equivalent to `clear` in godot.
38+
pub fn clear(&mut self) {
39+
self.as_inner().clear()
40+
}
41+
42+
/// Returns a deep copy of the dictionary. All nested arrays and dictionaries are duplicated and
43+
/// will not be shared with the original dictionary. Note that any `Object`-derived elements will
44+
/// still be shallow copied.
45+
///
46+
/// To create a shallow copy, use [`duplicate_shallow()`] instead. To create a new reference to
47+
/// the same array data, use [`share()`].
48+
///
49+
/// Equivalent to `dictionary.duplicate(true)` in godot.
50+
pub fn duplicate_deep(&self) -> Self {
51+
self.as_inner().duplicate(true)
52+
}
53+
54+
/// Returns a shallow copy of the dictionary. All dictionary keys and values are copied, but
55+
/// any reference types (such as `Array`, `Dictionary` and `Object`) will still refer to the
56+
/// same value.
57+
///
58+
/// To create a deep copy, use [`duplicate_deep()`] instead. To create a new reference to the
59+
/// same dictionary data, use [`share()`].
60+
///
61+
/// Equivalent to `dictionary.duplicate(false)` in godot.
62+
pub fn duplicate_shallow(&self) -> Self {
63+
self.as_inner().duplicate(false)
64+
}
65+
66+
/// Removes a key from the map, and returns the value associated with
67+
/// the key if the key was in the dictionary.
68+
pub fn remove(&mut self, key: impl ToVariant) -> Option<Variant> {
69+
let key = key.to_variant();
70+
let old_value = self.get(key.clone());
71+
self.as_inner().erase(key);
72+
old_value
73+
}
74+
75+
/// Returns the first key whose associated value is `value`, if one exists.
76+
///
77+
/// Unlike in godot, this will return `None` if the key does not exist
78+
/// and `Some(nil)` the key is `null`.
79+
pub fn find_key_by_value(&self, value: impl ToVariant) -> Option<Variant> {
80+
let key = self.as_inner().find_key(value.to_variant());
81+
82+
if !key.is_nil() || self.contains_key(key.clone()) {
83+
Some(key)
84+
} else {
85+
None
86+
}
87+
}
88+
89+
/// Returns the value at the key in the dictionary, if there is
90+
/// one.
91+
///
92+
/// Unlike `get` in godot, this will return `None` if there is
93+
/// no value with the given key.
94+
pub fn get(&self, key: impl ToVariant) -> Option<Variant> {
95+
let key = key.to_variant();
96+
if !self.contains_key(key.clone()) {
97+
return None;
98+
}
99+
100+
Some(self.get_or_nil(key))
101+
}
102+
103+
/// Returns the value at the key in the dictionary, or nil otherwise. This
104+
/// method does not let you differentiate `NIL` values stored as values from
105+
/// absent keys. If you need that, use `get()`.
106+
///
107+
/// This is equivalent to `get` in godot.
108+
pub fn get_or_nil(&self, key: impl ToVariant) -> Variant {
109+
self.as_inner().get(key.to_variant(), Variant::nil())
110+
}
111+
112+
/// Returns `true` if the dictionary contains the given key.
113+
///
114+
/// This is equivalent to `has` in godot.
115+
pub fn contains_key(&self, key: impl ToVariant) -> bool {
116+
let key = key.to_variant();
117+
self.as_inner().has(key)
118+
}
119+
120+
/// Returns `true` if the dictionary contains all the given keys.
121+
///
122+
/// This is equivalent to `has_all` in godot.
123+
pub fn contains_all_keys(&self, keys: Array) -> bool {
124+
self.as_inner().has_all(keys)
125+
}
126+
127+
/// Returns a 32-bit integer hash value representing the dictionary and its contents.
128+
pub fn hash(&self) -> u32 {
129+
self.as_inner().hash().try_into().unwrap()
130+
}
131+
132+
/// Creates a new `Array` containing all the keys currently in the dictionary.
133+
pub fn keys(&self) -> Array {
134+
self.as_inner().keys()
135+
}
136+
137+
/// Creates a new `Array` containing all the values currently in the dictionary.
138+
pub fn values(&self) -> Array {
139+
self.as_inner().values()
140+
}
141+
142+
/// Returns true if the dictionary is empty.
143+
pub fn is_empty(&self) -> bool {
144+
self.as_inner().is_empty()
145+
}
146+
147+
/// Copies all keys and values from other into self.
148+
///
149+
/// If overwrite is true, it will overwrite pre-existing keys. Otherwise
150+
/// it will not.
151+
///
152+
/// This is equivalent to `merge` in godot.
153+
pub fn extend_dictionary(&mut self, other: Self, overwrite: bool) {
154+
self.as_inner().merge(other, overwrite)
155+
}
156+
157+
/// Returns the number of entries in the dictionary.
158+
///
159+
/// This is equivalent to `size` in godot.
160+
pub fn len(&self) -> usize {
161+
self.as_inner().size().try_into().unwrap()
162+
}
163+
164+
/// Get the pointer corresponding to the given key in the dictionary,
165+
/// this pointer is null if there is no value at the given key.
166+
#[allow(dead_code)] // TODO: remove function if it turns out i'll never actually get any use out of it
167+
fn get_ptr(&self, key: impl ToVariant) -> *const Variant {
168+
let key = key.to_variant();
169+
unsafe {
170+
(interface_fn!(dictionary_operator_index_const))(self.sys_const(), key.var_sys_const())
171+
as *const Variant
172+
}
173+
}
174+
175+
/// Get the pointer corresponding to the given key in the dictionary,
176+
/// if there exists no value at the given key then a new one is created
177+
/// and initialized to a nil variant.
178+
fn get_ptr_mut(&mut self, key: impl ToVariant) -> *mut Variant {
179+
let key = key.to_variant();
180+
unsafe {
181+
let ptr =
182+
(interface_fn!(dictionary_operator_index))(self.sys_mut(), key.var_sys_const())
183+
as *mut Variant;
184+
// dictionary_operator_index initializes the value so it wont be null
185+
assert!(!ptr.is_null());
186+
// i think it might be safe to turn this into a &mut Variant?
187+
ptr
188+
}
189+
}
190+
191+
/// Insert a value at the given key, returning the value
192+
/// that previously was at that key if there was one.
193+
pub fn insert(&mut self, key: impl ToVariant, value: impl ToVariant) -> Option<Variant> {
194+
let key = key.to_variant();
195+
let old_value = self.get(key.clone());
196+
self.set(key, value);
197+
old_value
198+
}
199+
200+
/// Set a key to a given value
201+
pub fn set(&mut self, key: impl ToVariant, value: impl ToVariant) {
202+
let key = key.to_variant();
203+
unsafe {
204+
*self.get_ptr_mut(key) = value.to_variant();
205+
}
206+
}
207+
208+
#[doc(hidden)]
209+
pub fn as_inner(&self) -> inner::InnerDictionary {
210+
inner::InnerDictionary::from_outer(self)
211+
}
212+
}
213+
214+
/// Creates a `Dictionary` from the given `T`. Each key and value are
215+
/// converted to a `Variant`.
216+
impl<'a, 'b, K, V, T> From<T> for Dictionary
217+
where
218+
T: IntoIterator<Item = (&'a K, &'b V)>,
219+
K: ToVariant + 'a,
220+
V: ToVariant + 'b,
221+
{
222+
fn from(iterable: T) -> Self {
223+
iterable.into_iter().map(|(key,value)| (key.to_variant(), value.to_variant())).collect()
224+
}
225+
}
226+
227+
/// Convert this dictionary to a strongly typed rust `HashMap`. If the conversion
228+
/// fails for any key or value, an error is returned.
229+
///
230+
/// Will be replaced by a proper iteration implementation.
231+
impl<K: FromVariant + Eq + std::hash::Hash, V: FromVariant> TryFrom<&Dictionary> for HashMap<K, V> {
232+
type Error = VariantConversionError;
233+
234+
fn try_from(dictionary: &Dictionary) -> Result<Self, Self::Error> {
235+
// TODO: try to panic or something if modified while iterating
236+
// Though probably better to fix when implementing iteration proper
237+
dictionary
238+
.keys()
239+
.iter_shared()
240+
.zip(dictionary.values().iter_shared())
241+
.map(|(key, value)| Ok((K::try_from_variant(&key)?, V::try_from_variant(&value)?)))
242+
.collect()
243+
}
244+
}
245+
246+
/// Convert the keys of this dictionary to a strongly typed rust `HashSet`. If the
247+
/// conversion fails for any key, an error is returned.
248+
impl<K: FromVariant + Eq + std::hash::Hash> TryFrom<&Dictionary> for HashSet<K> {
249+
type Error = VariantConversionError;
250+
251+
fn try_from(dictionary: &Dictionary) -> Result<Self, Self::Error> {
252+
// TODO: try to panic or something if modified while iterating
253+
// Though probably better to fix when implementing iteration proper
254+
dictionary
255+
.keys()
256+
.iter_shared()
257+
.map(|key| K::try_from_variant(&key))
258+
.collect()
259+
}
260+
}
261+
262+
/// Inserts all key-values from the iterator into the dictionary,
263+
/// replacing values with existing keys with new values returned
264+
/// from the iterator.
265+
impl<K: ToVariant, V: ToVariant> Extend<(K, V)> for Dictionary {
266+
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
267+
for (k, v) in iter.into_iter() {
268+
self.set(k.to_variant(), v.to_variant())
269+
}
270+
}
271+
}
272+
273+
impl<K: ToVariant, V: ToVariant> FromIterator<(K, V)> for Dictionary {
274+
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
275+
let mut dict = Dictionary::new();
276+
for (k, v) in iter.into_iter() {
277+
dict.set(k.to_variant(), v.to_variant())
278+
}
279+
dict
280+
}
281+
}
282+
283+
impl_builtin_traits! {
284+
for Dictionary {
285+
Default => dictionary_construct_default;
286+
Drop => dictionary_destroy;
287+
PartialEq => dictionary_operator_equal;
288+
}
289+
}
290+
291+
impl GodotFfi for Dictionary {
292+
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
293+
}
294+
295+
impl fmt::Debug for Dictionary {
296+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297+
write!(f, "{:?}", self.to_variant().stringify())
298+
}
299+
}
300+
301+
/// Creates a new reference to the data in this dictionary. Changes to the original dictionary will be
302+
/// reflected in the copy and vice versa.
303+
///
304+
/// To create a (mostly) independent copy instead, see [`Dictionary::duplicate_shallow()`] and
305+
/// [`Dictionary::duplicate_deep()`].
306+
impl Share for Dictionary {
307+
fn share(&self) -> Self {
308+
unsafe {
309+
Self::from_sys_init(|self_ptr| {
310+
let ctor = sys::builtin_fn!(dictionary_construct_copy);
311+
let args = [self.sys_const()];
312+
ctor(self_ptr, args.as_ptr());
313+
})
314+
}
315+
}
316+
}
317+
318+
/// Creates a new dictionary with the given keys and values, the syntax mirrors
319+
/// godot's dictionary creation syntax.
320+
///
321+
/// Any value can be used as a key, but to use an expression you need to surround it
322+
/// in `()` or `{}`
323+
///
324+
/// Example
325+
/// ```rust, no_run
326+
/// # #[macro_use] extern crate godot_core;
327+
/// # fn main() {
328+
/// let key = "my_key";
329+
/// let d = dict! {
330+
/// "key1": 10,
331+
/// "another": Variant::nil(),
332+
/// key: true,
333+
/// (1 + 2): "final",
334+
/// }
335+
/// # }
336+
/// ```
337+
#[macro_export]
338+
macro_rules! dict {
339+
($($key:tt: $value:expr),* $(,)?) => {
340+
{
341+
let mut d = $crate::builtin::Dictionary::new();
342+
$(
343+
// otherwise `(1 + 2): true` would complain even though you can't write
344+
// 1 + 2: true
345+
#[allow(unused_parens)]
346+
d.set($key, $value);
347+
)*
348+
d
349+
}
350+
};
351+
}

godot-core/src/builtin/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod vector_macros;
3737

3838
mod arrays;
3939
mod color;
40+
mod dictionary;
4041
mod node_path;
4142
mod others;
4243
mod packed_array;
@@ -54,6 +55,7 @@ pub mod meta;
5455

5556
pub use arrays::*;
5657
pub use color::*;
58+
pub use dictionary::*;
5759
pub use node_path::*;
5860
pub use others::*;
5961
pub use packed_array::*;

godot-core/src/builtin/others.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ impl_builtin_stub!(Projection, OpaqueProjection);
2525
impl_builtin_stub!(Rid, OpaqueRid);
2626
impl_builtin_stub!(Callable, OpaqueCallable);
2727
impl_builtin_stub!(Signal, OpaqueSignal);
28-
impl_builtin_stub!(Dictionary, OpaqueDictionary);
2928

3029
#[repr(C)]
3130
struct InnerRect {

0 commit comments

Comments
 (0)