use std::fmt;
use godot_ffi as sys;
use sys::{GodotFfi, ffi_methods};
use crate::builtin::*;
use crate::meta;
use crate::meta::error::ConvertError;
use crate::meta::inspect::ElementType;
use crate::meta::shape::GodotShape;
use crate::meta::{AsArg, Element, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ToGodot};
#[derive(PartialEq)]
#[repr(transparent)] pub struct AnyDictionary {
pub(crate) dict: VarDictionary,
}
impl AnyDictionary {
pub(super) fn from_typed_or_untyped<K: Element, V: Element>(dict: Dictionary<K, V>) -> Self {
let inner = unsafe { std::mem::transmute::<Dictionary<K, V>, VarDictionary>(dict) };
Self { dict: inner }
}
pub(crate) fn new_untyped() -> Self {
Self {
dict: VarDictionary::default(),
}
}
fn from_opaque(opaque: sys::types::OpaqueDictionary) -> Self {
Self {
dict: VarDictionary::from_opaque(opaque),
}
}
pub fn at(&self, key: impl AsArg<Variant>) -> Variant {
self.dict.at(key)
}
pub fn get(&self, key: impl AsArg<Variant>) -> Option<Variant> {
self.dict.get(key)
}
#[doc(alias = "has")]
pub fn contains_key(&self, key: impl AsArg<Variant>) -> bool {
self.dict.contains_key(key)
}
#[doc(alias = "has_all")]
pub fn contains_all_keys(&self, keys: &VarArray) -> bool {
self.dict.contains_all_keys(keys)
}
#[doc(alias = "size")]
pub fn len(&self) -> usize {
self.dict.len()
}
pub fn is_empty(&self) -> bool {
self.dict.is_empty()
}
pub fn hash_u32(&self) -> u32 {
self.dict.hash_u32()
}
#[doc(alias = "find_key")]
pub fn find_key_by_value(&self, value: impl AsArg<Variant>) -> Option<Variant> {
self.dict.find_key_by_value(value)
}
pub fn clear(&mut self) {
self.dict.clear()
}
#[doc(alias = "erase")]
pub fn remove(&mut self, key: impl AsArg<Variant>) -> Option<Variant> {
self.dict.remove(key)
}
pub fn erase(&mut self, key: impl AsArg<Variant>) -> Option<Variant> {
self.remove(key)
}
#[doc(alias = "keys")]
pub fn keys_array(&self) -> AnyArray {
self.dict.as_inner().keys()
}
#[doc(alias = "values")]
pub fn values_array(&self) -> AnyArray {
self.dict.as_inner().values()
}
pub fn duplicate_shallow(&self) -> AnyDictionary {
self.dict.as_inner().duplicate(false)
}
pub fn duplicate_deep(&self) -> Self {
self.dict.as_inner().duplicate(true)
}
pub fn iter_shared(&self) -> AnyDictIter<'_> {
AnyDictIter {
inner: super::dictionary::DictIter::new(self),
}
}
pub fn keys_shared(&self) -> AnyDictKeys<'_> {
AnyDictKeys {
inner: super::dictionary::DictKeys::new(self),
}
}
pub fn values_shared(&self) -> AnyDictValues<'_> {
AnyDictValues {
inner: super::dictionary::DictValues::new(self),
}
}
#[doc(alias = "make_read_only")]
pub fn into_read_only(self) -> Self {
self.dict.as_inner().make_read_only();
self
}
pub fn is_read_only(&self) -> bool {
self.dict.is_read_only()
}
pub fn key_element_type(&self) -> ElementType {
#[cfg(since_api = "4.4")]
{
ElementType::get_or_compute_cached(
&self.dict.cached_key_type,
|| self.dict.as_inner().get_typed_key_builtin(),
|| self.dict.as_inner().get_typed_key_class_name(),
|| self.dict.as_inner().get_typed_key_script(),
)
}
#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
Self::polyfill_element_type(self.to_variant(), &self.dict.cached_key_type, "key")
}
pub fn value_element_type(&self) -> ElementType {
#[cfg(since_api = "4.4")]
{
ElementType::get_or_compute_cached(
&self.dict.cached_value_type,
|| self.dict.as_inner().get_typed_value_builtin(),
|| self.dict.as_inner().get_typed_value_class_name(),
|| self.dict.as_inner().get_typed_value_script(),
)
}
#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
Self::polyfill_element_type(self.to_variant(), &self.dict.cached_value_type, "value")
}
#[cfg(before_api = "4.4")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.4")))]
fn polyfill_element_type(
dict_var: Variant,
cache: &std::cell::OnceCell<ElementType>,
what: &str,
) -> ElementType {
if sys::GdextBuild::before_api("4.4") {
return cache.get_or_init(|| ElementType::Untyped).clone();
}
let builtin_method = format!("get_typed_{what}_builtin");
let class_name_method = format!("get_typed_{what}_class_name");
let script_method = format!("get_typed_{what}_script");
let get_typed_builtin = || {
dict_var
.call(&builtin_method, &[])
.try_to::<i64>()
.unwrap_or_else(|_| panic!("{builtin_method} returned non-integer"))
};
let get_typed_class_name = || {
dict_var
.call(&class_name_method, &[])
.try_to::<StringName>()
.unwrap_or_else(|_| panic!("{class_name_method} returned non-StringName"))
};
let get_typed_script = || dict_var.call(&script_method, &[]);
ElementType::get_or_compute_cached(
cache,
get_typed_builtin,
get_typed_class_name,
get_typed_script,
)
}
#[doc(hidden)]
pub unsafe fn as_inner_mut(&self) -> inner::InnerDictionary<'_> {
inner::InnerDictionary::from_outer(&self.dict)
}
pub fn try_cast_dictionary<K: Element, V: Element>(self) -> Result<Dictionary<K, V>, Self> {
let from_key_type = self.dict.key_element_type();
let from_value_type = self.dict.value_element_type();
let to_key_type = ElementType::of::<K>();
let to_value_type = ElementType::of::<V>();
if from_key_type == to_key_type && from_value_type == to_value_type {
let dict = unsafe { std::mem::transmute::<VarDictionary, Dictionary<K, V>>(self.dict) };
Ok(dict)
} else {
Err(self)
}
}
pub fn try_cast_var_dictionary(self) -> Result<VarDictionary, Self> {
self.try_cast_dictionary::<Variant, Variant>()
}
pub fn cast_dictionary<K: Element, V: Element>(self) -> Dictionary<K, V> {
let from_key = self.key_element_type();
let from_value = self.value_element_type();
self.try_cast_dictionary::<K, V>().unwrap_or_else(|_| {
panic!(
"cast_dictionary_or_panic: expected key type {:?} and value type {:?}, got {:?} and {:?}",
ElementType::of::<K>(),
ElementType::of::<V>(),
from_key,
from_value,
)
})
}
}
unsafe impl GodotFfi for AnyDictionary {
const VARIANT_TYPE: sys::ExtVariantType =
sys::ExtVariantType::Concrete(VariantType::DICTIONARY);
unsafe fn new_with_init(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
let dict = VarDictionary::new_uncached_type(init_fn);
Self { dict }
}
fn sys(&self) -> sys::GDExtensionConstTypePtr {
self.dict.sys()
}
fn sys_mut(&mut self) -> sys::GDExtensionTypePtr {
self.dict.sys_mut()
}
unsafe fn move_return_ptr(self, dst: sys::GDExtensionTypePtr, call_type: sys::PtrcallType) {
unsafe { self.dict.move_return_ptr(dst, call_type) }
}
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque;
fn new_from_sys;
fn new_with_uninit;
fn from_arg_ptr;
}
}
impl Clone for AnyDictionary {
fn clone(&self) -> Self {
Self {
dict: self.dict.clone(),
}
}
}
impl meta::sealed::Sealed for AnyDictionary {}
impl Element for AnyDictionary {}
impl GodotConvert for AnyDictionary {
type Via = Self;
fn godot_shape() -> GodotShape {
GodotShape::of_builtin::<Self>()
}
}
impl ToGodot for AnyDictionary {
type Pass = meta::ByValue;
fn to_godot(&self) -> meta::ToArg<'_, Self::Via, Self::Pass> {
self.clone()
}
fn to_variant(&self) -> Variant {
self.ffi_to_variant()
}
}
impl meta::FromGodot for AnyDictionary {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
}
impl fmt::Debug for AnyDictionary {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.dict.fmt(f)
}
}
impl fmt::Display for AnyDictionary {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.dict.fmt(f)
}
}
impl GodotType for AnyDictionary {
type Ffi = Self;
type ToFfi<'f>
= meta::RefArg<'f, AnyDictionary>
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, ConvertError> {
Ok(ffi)
}
}
impl GodotFfiVariant for AnyDictionary {
fn ffi_to_variant(&self) -> Variant {
VarDictionary::ffi_to_variant(&self.dict)
}
fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
let result = unsafe { VarDictionary::unchecked_from_variant(variant) };
result.map(|inner| Self { dict: inner })
}
}
impl<'a> IntoIterator for &'a AnyDictionary {
type Item = (Variant, Variant);
type IntoIter = AnyDictIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_shared()
}
}
pub struct AnyDictIter<'a> {
inner: super::dictionary::DictIter<'a, Variant, Variant>,
}
impl<'a> AnyDictIter<'a> {
pub fn typed<K: FromGodot, V: FromGodot>(self) -> super::dictionary::DictIter<'a, K, V> {
self.inner.typed()
}
}
impl Iterator for AnyDictIter<'_> {
type Item = (Variant, Variant);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct AnyDictKeys<'a> {
inner: super::dictionary::DictKeys<'a, Variant>,
}
impl<'a> AnyDictKeys<'a> {
pub fn typed<K: FromGodot>(self) -> super::dictionary::DictKeys<'a, K> {
self.inner.typed()
}
pub fn array(self) -> AnyArray {
self.inner.array()
}
}
impl Iterator for AnyDictKeys<'_> {
type Item = Variant;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
pub struct AnyDictValues<'a> {
inner: super::dictionary::DictValues<'a, Variant>,
}
impl<'a> AnyDictValues<'a> {
pub fn typed<V: FromGodot>(self) -> super::dictionary::DictValues<'a, V> {
self.inner.typed()
}
}
impl Iterator for AnyDictValues<'_> {
type Item = Variant;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}