Skip to content

Commit 867653c

Browse files
authored
Try #92:
2 parents c37d0ce + 253ae0b commit 867653c

File tree

9 files changed

+803
-2
lines changed

9 files changed

+803
-2
lines changed

godot-core/src/builtin/arrays.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ impl_builtin_traits! {
495495
Default => array_construct_default;
496496
Clone => array_construct_copy;
497497
Drop => array_destroy;
498+
Eq => array_operator_equal;
498499
}
499500
}
500501

godot-core/src/builtin/dictionary.rs

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

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 string;
@@ -53,6 +54,7 @@ pub mod meta;
5354

5455
pub use arrays::*;
5556
pub use color::*;
57+
pub use dictionary::*;
5658
pub use node_path::*;
5759
pub use others::*;
5860
pub use string::*;

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 {

godot-core/src/builtin/string.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use godot_ffi as sys;
1010
use sys::types::OpaqueString;
1111
use sys::{ffi_methods, interface_fn, GodotFfi};
1212

13+
use super::{Variant, VariantConversionError, ToVariant, FromVariant};
14+
1315
#[repr(C, align(8))]
1416
pub struct GodotString {
1517
opaque: OpaqueString,
@@ -147,6 +149,24 @@ impl fmt::Debug for GodotString {
147149
}
148150
}
149151

152+
impl ToVariant for &str {
153+
fn to_variant(&self) -> Variant {
154+
crate::builtin::GodotString::from(*self).to_variant()
155+
}
156+
}
157+
158+
impl ToVariant for String {
159+
fn to_variant(&self) -> Variant {
160+
crate::builtin::GodotString::from(self).to_variant()
161+
}
162+
}
163+
164+
impl FromVariant for String {
165+
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
166+
Ok(crate::builtin::GodotString::try_from_variant(variant)?.to_string())
167+
}
168+
}
169+
150170
// While this is a nice optimisation for ptrcalls, it's not easily possible
151171
// to pass in &GodotString when doing varcalls.
152172
/*

godot-core/src/builtin/variant/impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ mod impls {
133133
use super::*;
134134

135135
impl_variant_traits!(bool, bool_to_variant, bool_from_variant, Bool);
136+
impl_variant_traits!(Dictionary, dictionary_to_variant, dictionary_from_variant, Dictionary);
136137
impl_variant_traits!(Vector2, vector2_to_variant, vector2_from_variant, Vector2);
137138
impl_variant_traits!(Vector3, vector3_to_variant, vector3_from_variant, Vector3);
138139
impl_variant_traits!(Vector4, vector4_to_variant, vector4_from_variant, Vector4);

0 commit comments

Comments
 (0)