use std::cell::OnceCell;
use std::marker::PhantomData;
use std::{fmt, ptr};
use godot_ffi as sys;
use sys::types::OpaqueDictionary;
use sys::{GodotFfi, ffi_methods, interface_fn};
use super::any_dictionary::AnyDictionary;
use crate::builtin::{AnyArray, Array, VarArray, Variant, VariantType, inner};
use crate::meta;
use crate::meta::inspect::ElementType;
use crate::meta::shape::{GodotElementShape, GodotShape};
use crate::meta::{AsArg, Element, ExtVariantType, FromGodot, ToGodot};
use crate::registry::info::ParamMetadata;
use crate::registry::property::{BuiltinExport, Export, Var};
pub struct Dictionary<K: Element, V: Element> {
opaque: OpaqueDictionary,
_phantom: PhantomData<(K, V)>,
pub(super) cached_key_type: OnceCell<ElementType>,
pub(super) cached_value_type: OnceCell<ElementType>,
}
pub type VarDictionary = Dictionary<Variant, Variant>;
impl<K: Element, V: Element> Dictionary<K, V> {
pub(super) fn from_opaque(opaque: OpaqueDictionary) -> Self {
Self {
opaque,
_phantom: PhantomData,
cached_key_type: OnceCell::new(),
cached_value_type: OnceCell::new(),
}
}
pub(super) fn new_uncached_type(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
let mut result = unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(dictionary_construct_default);
ctor(self_ptr, std::ptr::null_mut());
})
};
init_fn(result.sys_mut());
result
}
pub fn new() -> Self {
Self::default()
}
pub fn at(&self, key: impl AsArg<K>) -> V {
meta::arg_into_ref!(key: K);
let key_variant = key.to_variant();
if self.as_inner().has(&key_variant) {
self.get_or_panic(key_variant)
} else {
panic!("key {key_variant:?} missing in dictionary: {self:?}")
}
}
pub fn get(&self, key: impl AsArg<K>) -> Option<V> {
meta::arg_into_ref!(key: K);
let key_variant = key.to_variant();
if self.as_inner().has(&key_variant) {
Some(self.get_or_panic(key_variant))
} else {
None
}
}
fn get_or_panic(&self, key: Variant) -> V {
V::from_variant(&self.as_inner().get(&key, &Variant::nil()))
}
fn take_old_value(&self, key_variant: &Variant) -> Option<V> {
self.as_inner()
.has(key_variant)
.then(|| self.get_or_panic(key_variant.clone()))
}
#[doc(alias = "get_or_add")]
pub fn get_or_insert(&mut self, key: impl AsArg<K>, default: impl AsArg<V>) -> V {
self.balanced_ensure_mutable();
meta::arg_into_ref!(key: K);
meta::arg_into_ref!(default: V);
let key_variant = key.to_variant();
#[cfg(since_api = "4.3")]
{
let default_variant = default.to_variant();
let result = self.as_inner().get_or_add(&key_variant, &default_variant);
V::from_variant(&result)
}
#[cfg(before_api = "4.3")]
{
if self.as_inner().has(&key_variant) {
self.get_or_panic(key_variant)
} else {
let default_variant = default.to_variant();
unsafe { self.set_variant(key_variant, default_variant.clone()) };
V::from_variant(&default_variant)
}
}
}
#[doc(alias = "has")]
pub fn contains_key(&self, key: impl AsArg<K>) -> bool {
meta::arg_into_ref!(key: K);
let key = key.to_variant();
self.as_inner().has(&key)
}
#[doc(alias = "has_all")]
pub fn contains_all_keys(&self, keys: &VarArray) -> bool {
self.as_inner().has_all(keys)
}
#[doc(alias = "size")]
pub fn len(&self) -> usize {
self.as_inner().size().try_into().unwrap()
}
pub fn is_empty(&self) -> bool {
self.as_inner().is_empty()
}
#[doc(alias = "find_key")]
pub fn find_key_by_value(&self, value: impl AsArg<V>) -> Option<K>
where
K: FromGodot,
{
meta::arg_into_ref!(value: V);
let key = self.as_inner().find_key(&value.to_variant());
if !key.is_nil() || self.as_inner().has(&key) {
Some(K::from_variant(&key))
} else {
None
}
}
pub fn clear(&mut self) {
self.balanced_ensure_mutable();
self.as_inner().clear()
}
pub fn set(&mut self, key: impl AsArg<K>, value: impl AsArg<V>) {
self.balanced_ensure_mutable();
meta::arg_into_ref!(key: K);
meta::arg_into_ref!(value: V);
unsafe { self.set_variant(key.to_variant(), value.to_variant()) };
}
#[doc(hidden)]
pub fn __macro_set_direct<Ke, Ve>(&mut self, key: Ke, value: Ve)
where
Ke: meta::AsDirectElement<K>,
Ve: meta::AsDirectElement<V>,
{
self.set(key, value)
}
#[must_use]
pub fn insert(&mut self, key: impl AsArg<K>, value: impl AsArg<V>) -> Option<V> {
self.balanced_ensure_mutable();
meta::arg_into_ref!(key: K);
meta::arg_into_ref!(value: V);
let key_variant = key.to_variant();
let old_value = self.take_old_value(&key_variant);
unsafe { self.set_variant(key_variant, value.to_variant()) };
old_value
}
#[doc(alias = "erase")]
pub fn remove(&mut self, key: impl AsArg<K>) -> Option<V> {
self.balanced_ensure_mutable();
meta::arg_into_ref!(key: K);
let key_variant = key.to_variant();
let old_value = self.take_old_value(&key_variant);
self.as_inner().erase(&key_variant);
old_value
}
crate::declare_hash_u32_method! {
}
#[doc(alias = "keys")]
pub fn keys_array(&self) -> Array<K> {
self.as_inner().keys().cast_array()
}
#[doc(alias = "values")]
pub fn values_array(&self) -> Array<V> {
self.as_inner().values().cast_array()
}
#[doc(alias = "merge")]
pub fn extend_dictionary(&mut self, other: &Self, overwrite: bool) {
self.balanced_ensure_mutable();
self.as_inner().merge(other, overwrite)
}
pub fn duplicate_deep(&self) -> Self {
self.as_inner().duplicate(true).cast_dictionary::<K, V>()
}
pub fn duplicate_shallow(&self) -> Self {
self.as_inner().duplicate(false).cast_dictionary::<K, V>()
}
pub fn iter_shared(&self) -> DictIter<'_, K, V> {
DictIter::new(self)
}
pub fn keys_shared(&self) -> DictKeys<'_, K> {
DictKeys::new(self)
}
pub fn values_shared(&self) -> DictValues<'_, V> {
DictValues::new(self)
}
#[doc(alias = "make_read_only")]
pub fn into_read_only(self) -> Self {
self.as_inner().make_read_only();
self
}
pub fn is_read_only(&self) -> bool {
self.as_inner().is_read_only()
}
pub fn upcast_any_dictionary(self) -> AnyDictionary {
AnyDictionary::from_typed_or_untyped(self)
}
fn balanced_ensure_mutable(&self) {
sys::balanced_assert!(
!self.is_read_only(),
"mutating operation on read-only dictionary"
);
}
#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerDictionary<'_> {
inner::InnerDictionary::from_outer_typed(self)
}
fn get_ptr_mut(&mut self, key: Variant) -> sys::GDExtensionVariantPtr {
unsafe { interface_fn!(dictionary_operator_index)(self.sys_mut(), key.var_sys()) }
}
unsafe fn set_variant(&mut self, key: Variant, value: Variant) {
let ptr = self.get_ptr_mut(key);
unsafe { value.move_into_var_ptr(ptr) };
}
fn with_cache(self, source: &Self) -> Self {
ElementType::transfer_cache(&source.cached_key_type, &self.cached_key_type);
ElementType::transfer_cache(&source.cached_value_type, &self.cached_value_type);
self
}
#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
fn with_checked_type(self) -> Result<Self, meta::error::ConvertError> {
use crate::meta::error::{DictionaryMismatch, FromGodotError};
let actual_key = self.key_element_type();
let actual_value = self.value_element_type();
let expected_key = ElementType::of::<K>();
let expected_value = ElementType::of::<V>();
if actual_key.is_compatible_with(&expected_key)
&& actual_value.is_compatible_with(&expected_value)
{
Ok(self)
} else {
let mismatch = DictionaryMismatch {
expected_key,
expected_value,
actual_key,
actual_value,
};
Err(FromGodotError::BadDictionaryType(mismatch).into_error(self))
}
}
fn as_any_ref(&self) -> &AnyDictionary {
unsafe { std::mem::transmute::<&Dictionary<K, V>, &AnyDictionary>(self) }
}
fn as_any_mut(&mut self) -> &mut AnyDictionary {
unsafe { std::mem::transmute::<&mut Dictionary<K, V>, &mut AnyDictionary>(self) }
}
pub(super) unsafe fn unchecked_from_variant(
variant: &Variant,
) -> Result<Self, meta::error::ConvertError> {
use crate::builtin::VariantType;
use crate::meta::error::FromVariantError;
let variant_type = variant.get_type();
if variant_type != VariantType::DICTIONARY {
return Err(FromVariantError::BadType {
expected: VariantType::DICTIONARY,
actual: variant_type,
}
.into_error(variant.clone()));
}
let result = unsafe {
Self::new_with_uninit(|self_ptr| {
let converter = sys::builtin_fn!(dictionary_from_variant);
converter(self_ptr, sys::SysPtr::force_mut(variant.var_sys()));
})
};
Ok(result)
}
fn init_inner_type_with(&mut self, dictionary_set_typed: DictionarySetTyped) {
let key_elem_ty = ElementType::of::<K>();
let value_elem_ty = ElementType::of::<V>();
if !key_elem_ty.is_typed() && !value_elem_ty.is_typed() {
return;
}
self.cached_key_type.get_or_init(|| key_elem_ty);
self.cached_value_type.get_or_init(|| value_elem_ty);
let script = Variant::nil();
let empty_string_name = crate::builtin::StringName::default();
let key_class_name = key_elem_ty.class_name_sys_or(&empty_string_name);
let value_class_name = value_elem_ty.class_name_sys_or(&empty_string_name);
unsafe {
dictionary_set_typed(
self.sys_mut(),
key_elem_ty.variant_type().sys(),
key_class_name,
script.var_sys(),
value_elem_ty.variant_type().sys(),
value_class_name,
script.var_sys(),
);
}
}
#[cfg(since_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.4")))]
fn init_inner_type(&mut self) {
self.init_inner_type_with(interface_fn!(dictionary_set_typed));
}
#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
fn init_inner_type(&mut self) {
if !sys::GdextBuild::since_api("4.4") {
self.cached_key_type.get_or_init(|| ElementType::Untyped);
self.cached_value_type.get_or_init(|| ElementType::Untyped);
return;
}
let fptr = unsafe { sys::get_ffi_ptr_by_cstr(b"dictionary_set_typed\0") }
.expect("dictionary_set_typed should be available on Godot 4.4+");
let dictionary_set_typed: DictionarySetTyped = unsafe { std::mem::transmute(fptr) };
self.init_inner_type_with(dictionary_set_typed);
}
}
impl<K: Element> Dictionary<K, Variant> {
pub fn get_or_nil(&self, key: impl AsArg<K>) -> Variant {
meta::arg_into_ref!(key: K);
let key_variant = key.to_variant();
self.as_inner().get(&key_variant, &Variant::nil())
}
}
unsafe impl<K: Element, V: Element> GodotFfi for Dictionary<K, V> {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::DICTIONARY);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
fn new_from_sys;
fn new_with_uninit;
fn sys;
fn sys_mut;
fn from_arg_ptr;
fn move_return_ptr;
}
unsafe fn new_with_init(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
Self::new_uncached_type(init_fn)
}
}
impl<K: Element, V: Element> std::ops::Deref for Dictionary<K, V> {
type Target = AnyDictionary;
fn deref(&self) -> &Self::Target {
self.as_any_ref()
}
}
impl<K: Element, V: Element> std::ops::DerefMut for Dictionary<K, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_any_mut()
}
}
sys::static_assert_eq_size_align!(Dictionary<i64, bool>, VarDictionary);
sys::static_assert_eq_size_align!(Dictionary<crate::builtin::GString, f32>, VarDictionary);
sys::static_assert_eq_size_align!(VarDictionary, AnyDictionary);
impl<K: Element, V: Element> Default for Dictionary<K, V> {
#[inline]
fn default() -> Self {
let mut dict = unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(dictionary_construct_default);
ctor(self_ptr, ptr::null_mut())
})
};
dict.init_inner_type();
dict
}
}
impl<K: Element, V: Element> Drop for Dictionary<K, V> {
fn drop(&mut self) {
unsafe { sys::builtin_fn!(dictionary_destroy)(self.sys_mut()) }
}
}
impl<K: Element, V: Element> PartialEq for Dictionary<K, V> {
fn eq(&self, other: &Self) -> bool {
unsafe {
let mut result = false;
sys::builtin_call! {
dictionary_operator_equal(self.sys(), other.sys(), result.sys_mut())
}
result
}
}
}
impl<K: Element, V: Element> fmt::Debug for Dictionary<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.to_variant().stringify())
}
}
impl<K: Element + fmt::Display, V: Element + fmt::Display> fmt::Display for Dictionary<K, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{{ ")?;
for (count, (key, value)) in self.iter_shared().enumerate() {
if count != 0 {
write!(f, ", ")?;
}
write!(f, "{key}: {value}")?;
}
write!(f, " }}")
}
}
impl<K: Element, V: Element> Clone for Dictionary<K, V> {
fn clone(&self) -> Self {
let result = unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(dictionary_construct_copy);
let args = [self.sys()];
ctor(self_ptr, args.as_ptr());
})
};
result.with_cache(self)
}
}
impl<'a, K: Element, V: Element> IntoIterator for &'a Dictionary<K, V> {
type Item = (K, V);
type IntoIter = DictIter<'a, K, V>;
fn into_iter(self) -> Self::IntoIter {
self.iter_shared()
}
}
impl<K: Element, V: Element> Extend<(K, V)> for Dictionary<K, V> {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
for (k, v) in iter.into_iter() {
self.balanced_ensure_mutable();
unsafe { self.set_variant(k.to_variant(), v.to_variant()) };
}
}
}
impl<K: Element, V: Element> FromIterator<(K, V)> for Dictionary<K, V> {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let mut dict = Dictionary::new();
dict.extend(iter);
dict
}
}
impl<K: Element, V: Element> meta::sealed::Sealed for Dictionary<K, V> {}
impl<K: Element, V: Element> meta::GodotConvert for Dictionary<K, V> {
type Via = Self;
fn godot_shape() -> GodotShape {
if !is_dictionary_typed::<K, V>() {
return GodotShape::Builtin {
variant_type: VariantType::DICTIONARY,
metadata: ParamMetadata::NONE,
};
}
GodotShape::TypedDictionary {
key: GodotElementShape::new(K::godot_shape()),
value: GodotElementShape::new(V::godot_shape()),
}
}
}
impl<K: Element, V: Element> ToGodot for Dictionary<K, V> {
type Pass = meta::ByRef;
fn to_godot(&self) -> &Self::Via {
self
}
}
impl<K: Element, V: Element> FromGodot for Dictionary<K, V> {
fn try_from_godot(via: Self::Via) -> Result<Self, meta::error::ConvertError> {
Ok(via)
}
}
impl<K: Element, V: Element> meta::GodotFfiVariant for Dictionary<K, V> {
fn ffi_to_variant(&self) -> Variant {
unsafe {
Variant::new_with_var_uninit(|variant_ptr| {
let converter = sys::builtin_fn!(dictionary_to_variant);
converter(variant_ptr, sys::SysPtr::force_mut(self.sys()));
})
}
}
fn ffi_from_variant(variant: &Variant) -> Result<Self, meta::error::ConvertError> {
let result = unsafe { Self::unchecked_from_variant(variant) }?;
#[cfg(since_api = "4.4")]
{
result.with_checked_type()
}
#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
Ok(result)
}
}
impl<K: Element, V: Element> meta::GodotType for Dictionary<K, V> {
type Ffi = Self;
type ToFfi<'f>
= meta::RefArg<'f, Dictionary<K, V>>
where
Self: 'f;
fn to_ffi(&self) -> Self::ToFfi<'_> {
meta::RefArg::new(self)
}
fn into_ffi(self) -> Self::Ffi {
self
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, meta::error::ConvertError> {
Ok(ffi)
}
}
impl Element for VarDictionary {}
#[inline]
fn is_dictionary_typed<K: Element, V: Element>() -> bool {
meta::element_variant_type::<K>() != VariantType::NIL
|| meta::element_variant_type::<V>() != VariantType::NIL
}
impl<K: Element, V: Element> Var for Dictionary<K, V> {
type PubType = Self;
fn var_get(field: &Self) -> Self::Via {
field.clone()
}
fn var_set(field: &mut Self, value: Self::Via) {
*field = value;
}
fn var_pub_get(field: &Self) -> Self::PubType {
field.clone()
}
fn var_pub_set(field: &mut Self, value: Self::PubType) {
*field = value;
}
}
impl<K, V> Export for Dictionary<K, V>
where
K: Element + Export,
V: Element + Export,
{
}
impl<K: Element, V: Element> BuiltinExport for Dictionary<K, V> {}
struct DictionaryIter<'a> {
last_key: Option<Variant>,
dictionary: &'a AnyDictionary,
is_first: bool,
next_idx: usize,
}
impl<'a> DictionaryIter<'a> {
fn new(dictionary: &'a AnyDictionary) -> Self {
Self {
last_key: None,
dictionary,
is_first: true,
next_idx: 0,
}
}
fn next_key(&mut self) -> Option<Variant> {
let new_key = if self.is_first {
self.is_first = false;
Self::call_init(self.dictionary)
} else {
Self::call_next(self.dictionary, self.last_key.take()?)
};
if self.next_idx < self.dictionary.len() {
self.next_idx += 1;
}
self.last_key.clone_from(&new_key);
new_key
}
fn next_key_value(&mut self) -> Option<(Variant, Variant)> {
let key = self.next_key()?;
let inner = unsafe { self.dictionary.as_inner_mut() };
if !inner.has(&key) {
return None;
}
let value = inner.get(&key, &Variant::nil());
Some((key, value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = usize::saturating_sub(self.dictionary.len(), self.next_idx);
(remaining, Some(remaining))
}
fn call_init(dictionary: &AnyDictionary) -> Option<Variant> {
let variant: Variant = Variant::nil();
let iter_fn = |dictionary, next_value: sys::GDExtensionVariantPtr, valid| unsafe {
interface_fn!(variant_iter_init)(dictionary, sys::SysPtr::as_uninit(next_value), valid)
};
Self::ffi_iterate(iter_fn, dictionary, variant)
}
fn call_next(dictionary: &AnyDictionary, last_key: Variant) -> Option<Variant> {
let iter_fn = |dictionary, next_value, valid| unsafe {
interface_fn!(variant_iter_next)(dictionary, next_value, valid)
};
Self::ffi_iterate(iter_fn, dictionary, last_key)
}
fn ffi_iterate(
iter_fn: unsafe fn(
sys::GDExtensionConstVariantPtr,
sys::GDExtensionVariantPtr,
*mut sys::GDExtensionBool,
) -> sys::GDExtensionBool,
dictionary: &AnyDictionary,
mut next_value: Variant,
) -> Option<Variant> {
let dictionary = dictionary.to_variant();
let mut valid_u8: u8 = 0;
let has_next = unsafe {
iter_fn(
dictionary.var_sys(),
next_value.var_sys_mut(),
ptr::addr_of_mut!(valid_u8),
)
};
let valid = u8_to_bool(valid_u8);
let has_next = u8_to_bool(has_next);
if has_next {
assert!(valid);
Some(next_value)
} else {
None
}
}
}
pub struct DictIter<'a, K, V> {
iter: DictionaryIter<'a>,
_kv: PhantomData<(K, V)>,
}
impl<'a, K, V> DictIter<'a, K, V> {
pub(super) fn new(dictionary: &'a AnyDictionary) -> Self {
Self {
iter: DictionaryIter::new(dictionary),
_kv: PhantomData,
}
}
}
impl<'a> DictIter<'a, Variant, Variant> {
pub fn typed<K: FromGodot, V: FromGodot>(self) -> DictIter<'a, K, V> {
DictIter {
iter: self.iter,
_kv: PhantomData,
}
}
}
impl<K: FromGodot, V: FromGodot> Iterator for DictIter<'_, K, V> {
type Item = (K, V);
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next_key_value()
.map(|(key, value)| (K::from_variant(&key), V::from_variant(&value)))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct DictKeys<'a, K> {
iter: DictionaryIter<'a>,
_k: PhantomData<K>,
}
impl<'a, K> DictKeys<'a, K> {
pub(super) fn new(dictionary: &'a AnyDictionary) -> Self {
Self {
iter: DictionaryIter::new(dictionary),
_k: PhantomData,
}
}
pub fn array(self) -> AnyArray {
assert!(
self.iter.is_first,
"Keys::array() can only be called before iteration has started"
);
self.iter.dictionary.keys_array()
}
}
impl<'a> DictKeys<'a, Variant> {
pub fn typed<K: FromGodot>(self) -> DictKeys<'a, K> {
DictKeys {
iter: self.iter,
_k: PhantomData,
}
}
}
impl<K: FromGodot> Iterator for DictKeys<'_, K> {
type Item = K;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next_key().map(|k| K::from_variant(&k))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct DictValues<'a, V> {
iter: DictionaryIter<'a>,
_v: PhantomData<V>,
}
impl<'a, V> DictValues<'a, V> {
pub(super) fn new(dictionary: &'a AnyDictionary) -> Self {
Self {
iter: DictionaryIter::new(dictionary),
_v: PhantomData,
}
}
}
impl<'a> DictValues<'a, Variant> {
pub fn typed<V: FromGodot>(self) -> DictValues<'a, V> {
DictValues {
iter: self.iter,
_v: PhantomData,
}
}
}
impl<V: FromGodot> Iterator for DictValues<'_, V> {
type Item = V;
fn next(&mut self) -> Option<Self::Item> {
self.iter
.next_key_value()
.map(|(_, value)| V::from_variant(&value))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
fn u8_to_bool(u: u8) -> bool {
match u {
0 => false,
1 => true,
_ => panic!("Invalid boolean value {u}"),
}
}
#[macro_export]
macro_rules! dict {
($($key:expr => $value:expr),* $(,)?) => {
{
let mut d = $crate::builtin::Dictionary::new();
$(
d.set($key, $value);
)*
d
}
};
}
#[macro_export]
macro_rules! idict {
($($key:expr => $value:expr),* $(,)?) => {
{
let mut d = $crate::builtin::Dictionary::new();
$(
d.__macro_set_direct($key, $value);
)*
d
}
};
}
#[macro_export]
macro_rules! vdict {
($($key:expr => $value:expr_2021),* $(,)?) => {
{
let mut dict = $crate::builtin::VarDictionary::new();
$(
dict.set($key, $value);
)*
dict
}
};
($($key:tt: $value:expr),* $(,)?) => {
{
const _: () = $crate::__deprecated::vdict_colon_syntax();
let mut d = $crate::builtin::VarDictionary::new();
$(
#[allow(unused_parens)]
d.set($key, $value);
)*
d
}
};
}
type DictionarySetTyped = unsafe extern "C" fn(
sys::GDExtensionTypePtr,
sys::GDExtensionVariantType,
sys::GDExtensionConstStringNamePtr,
sys::GDExtensionConstVariantPtr,
sys::GDExtensionVariantType,
sys::GDExtensionConstStringNamePtr,
sys::GDExtensionConstVariantPtr,
);