use std::cell::OnceCell;
use std::marker::PhantomData;
use std::{cmp, fmt};
use godot_ffi as sys;
use sys::{GodotFfi, ffi_methods, interface_fn};
use crate::builtin::iter::ArrayFunctionalOps;
use crate::builtin::*;
use crate::meta;
use crate::meta::error::{ArrayMismatch, ConvertError, FromGodotError, FromVariantError};
use crate::meta::inspect::ElementType;
use crate::meta::shape::{GodotElementShape, GodotShape};
use crate::meta::signed_range::SignedRange;
use crate::meta::{
AsArg, ClassId, Element, ExtVariantType, FromGodot, GodotConvert, GodotFfiVariant, GodotType,
RefArg, ToGodot, element_variant_type,
};
use crate::obj::{Bounds, DynGd, Gd, GodotClass, bounds};
use crate::registry::info::{ParamMetadata, PropertyHintInfo};
use crate::registry::property::{BuiltinExport, Export, Var};
pub struct Array<T: Element> {
opaque: sys::types::OpaqueArray,
_phantom: PhantomData<T>,
pub(super) cached_element_type: OnceCell<ElementType>,
}
pub(super) struct ImmutableInnerArray<'a> {
inner: inner::InnerArray<'a>,
}
impl<'a> std::ops::Deref for ImmutableInnerArray<'a> {
type Target = inner::InnerArray<'a>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: Element> std::ops::Deref for Array<T> {
type Target = AnyArray;
fn deref(&self) -> &Self::Target {
self.as_any_ref()
}
}
impl<T: Element> std::ops::DerefMut for Array<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_any_mut()
}
}
sys::static_assert_eq_size_align!(Array<i64>, VarArray);
sys::static_assert_eq_size_align!(Array<GString>, VarArray);
sys::static_assert_eq_size_align!(VarArray, AnyArray);
pub type VarArray = Array<Variant>;
impl<T: Element> Array<T> {
pub(super) fn from_opaque(opaque: sys::types::OpaqueArray) -> Self {
Self {
opaque,
_phantom: PhantomData,
cached_element_type: OnceCell::new(),
}
}
pub fn new() -> Self {
Self::default()
}
pub fn at(&self, index: usize) -> T {
let ptr = self.ptr(index);
let variant = unsafe { Variant::borrow_var_sys(ptr) };
T::from_variant(variant)
}
pub fn get(&self, index: usize) -> Option<T> {
let ptr = self.ptr_or_null(index);
if ptr.is_null() {
None
} else {
let variant = unsafe { Variant::borrow_var_sys(ptr) };
Some(T::from_variant(variant))
}
}
pub fn contains(&self, value: impl AsArg<T>) -> bool {
meta::arg_into_ref!(value: T);
self.as_inner().has(&value.to_variant())
}
pub fn count(&self, value: impl AsArg<T>) -> usize {
meta::arg_into_ref!(value: T);
to_usize(self.as_inner().count(&value.to_variant()))
}
#[doc(alias = "first")]
pub fn front(&self) -> Option<T> {
(!self.is_empty()).then(|| {
let variant = self.as_inner().front();
T::from_variant(&variant)
})
}
#[doc(alias = "last")]
pub fn back(&self) -> Option<T> {
(!self.is_empty()).then(|| {
let variant = self.as_inner().back();
T::from_variant(&variant)
})
}
pub fn set(&mut self, index: usize, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
let ptr_mut = self.ptr_mut(index);
meta::arg_into_ref!(value: T);
let variant = value.to_variant();
unsafe { variant.move_into_var_ptr(ptr_mut) };
}
#[doc(alias = "append")]
#[doc(alias = "push_back")]
pub fn push(&mut self, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
meta::arg_into_ref!(value: T);
let mut inner = unsafe { self.as_inner_mut() };
inner.push_back(&value.to_variant());
}
#[doc(hidden)]
pub fn __macro_push_direct<E: meta::AsDirectElement<T>>(&mut self, value: E) {
self.push(value)
}
pub fn push_front(&mut self, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
meta::arg_into_ref!(value: T);
let mut inner_array = unsafe { self.as_inner_mut() };
inner_array.push_front(&value.to_variant());
}
#[doc(alias = "pop_back")]
pub fn pop(&mut self) -> Option<T> {
self.balanced_ensure_mutable();
(!self.is_empty()).then(|| {
let variant = unsafe { self.as_inner_mut() }.pop_back();
T::from_variant(&variant)
})
}
pub fn pop_front(&mut self) -> Option<T> {
self.balanced_ensure_mutable();
(!self.is_empty()).then(|| {
let variant = unsafe { self.as_inner_mut() }.pop_front();
T::from_variant(&variant)
})
}
pub fn insert(&mut self, index: usize, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
let len = self.len();
assert!(
index <= len,
"Array insertion index {index} is out of bounds: length is {len}",
);
meta::arg_into_ref!(value: T);
unsafe { self.as_inner_mut() }.insert(to_i64(index), &value.to_variant());
}
#[doc(alias = "pop_at")]
pub fn remove(&mut self, index: usize) -> T {
self.balanced_ensure_mutable();
self.check_bounds(index);
let variant = unsafe { self.as_inner_mut() }.pop_at(to_i64(index));
T::from_variant(&variant)
}
pub fn erase(&mut self, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
meta::arg_into_ref!(value: T);
unsafe { self.as_inner_mut() }.erase(&value.to_variant());
}
pub fn fill(&mut self, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
meta::arg_into_ref!(value: T);
unsafe { self.as_inner_mut() }.fill(&value.to_variant());
}
pub fn resize(&mut self, new_size: usize, value: impl AsArg<T>) {
self.balanced_ensure_mutable();
let original_size = self.len();
unsafe { self.as_inner_mut() }.resize(to_i64(new_size));
meta::arg_into_ref!(value: T);
for i in original_size..new_size {
let variant = value.to_variant();
let ptr_mut = self.ptr_mut(i);
unsafe { variant.move_into_var_ptr(ptr_mut) };
}
}
pub fn extend_array(&mut self, other: &Array<T>) {
self.balanced_ensure_mutable();
let mut inner_self = unsafe { self.as_inner_mut() };
inner_self.append_array(other);
}
pub fn duplicate_shallow(&self) -> Self {
self.as_inner().duplicate(false).cast_array::<T>()
}
pub fn duplicate_deep(&self) -> Self {
self.as_inner().duplicate(true).cast_array::<T>()
}
#[doc(alias = "slice")]
pub fn subarray_shallow(&self, range: impl SignedRange, step: Option<i32>) -> Self {
self.subarray_impl(range, step, false)
}
#[doc(alias = "slice")]
pub fn subarray_deep(&self, range: impl SignedRange, step: Option<i32>) -> Self {
self.subarray_impl(range, step, true)
}
fn subarray_impl(&self, range: impl SignedRange, step: Option<i32>, deep: bool) -> Self {
assert_ne!(step, Some(0), "subarray: step cannot be zero");
let step = step.unwrap_or(1);
let (begin, end) = range.signed();
let end = end.unwrap_or(i32::MAX as i64);
self.as_inner()
.slice(begin, end, step as i64, deep)
.cast_array::<T>()
}
pub fn iter_shared(&self) -> ArrayIter<'_, T> {
ArrayIter {
array: self,
next_idx: 0,
}
}
pub fn min(&self) -> Option<T> {
let min = self.as_inner().min();
(!min.is_nil()).then(|| T::from_variant(&min))
}
pub fn max(&self) -> Option<T> {
let max = self.as_inner().max();
(!max.is_nil()).then(|| T::from_variant(&max))
}
pub fn pick_random(&self) -> Option<T> {
(!self.is_empty()).then(|| {
let variant = self.as_inner().pick_random();
T::from_variant(&variant)
})
}
pub fn find(&self, value: impl AsArg<T>, from: Option<usize>) -> Option<usize> {
meta::arg_into_ref!(value: T);
let from = to_i64(from.unwrap_or(0));
let index = self.as_inner().find(&value.to_variant(), from);
if index >= 0 {
Some(index.try_into().unwrap())
} else {
None
}
}
pub fn rfind(&self, value: impl AsArg<T>, from: Option<usize>) -> Option<usize> {
meta::arg_into_ref!(value: T);
let from = from.map(to_i64).unwrap_or(-1);
let index = self.as_inner().rfind(&value.to_variant(), from);
if index >= 0 {
Some(to_usize(index))
} else {
None
}
}
pub fn bsearch(&self, value: impl AsArg<T>) -> usize {
meta::arg_into_ref!(value: T);
to_usize(self.as_inner().bsearch(&value.to_variant(), true))
}
pub fn bsearch_by<F>(&self, mut func: F) -> Result<usize, usize>
where
F: FnMut(&T) -> cmp::Ordering + 'static,
{
if self.is_empty() {
return Err(0);
}
let ignored_value = self.at(0);
let ignored_value = meta::owned_into_arg(ignored_value);
let godot_comparator = |args: &[&Variant]| {
let value = T::from_variant(args[0]);
let is_less = matches!(func(&value), cmp::Ordering::Less);
is_less.to_variant()
};
let debug_name = std::any::type_name::<F>();
let index = Callable::with_scoped_fn(debug_name, godot_comparator, |pred| {
self.functional_ops().bsearch_custom(ignored_value, pred)
});
if let Some(value_at_index) = self.get(index)
&& func(&value_at_index) == cmp::Ordering::Equal
{
return Ok(index);
}
Err(index)
}
pub fn sort_unstable_by<F>(&mut self, mut func: F)
where
F: FnMut(&T, &T) -> cmp::Ordering,
{
self.balanced_ensure_mutable();
let godot_comparator = |args: &[&Variant]| {
let lhs = T::from_variant(args[0]);
let rhs = T::from_variant(args[1]);
let is_less = matches!(func(&lhs, &rhs), cmp::Ordering::Less);
is_less.to_variant()
};
let debug_name = std::any::type_name::<F>();
Callable::with_scoped_fn(debug_name, godot_comparator, |pred| {
self.sort_unstable_custom(pred)
});
}
pub fn functional_ops(&self) -> ArrayFunctionalOps<'_, T> {
ArrayFunctionalOps::new(self)
}
#[doc(alias = "make_read_only")]
pub fn into_read_only(self) -> Self {
unsafe { self.as_inner_mut() }.make_read_only();
self
}
pub fn upcast_any_array(self) -> AnyArray {
AnyArray::from_typed_or_untyped(self)
}
pub fn to_packed_array(&self) -> PackedArray<T>
where
T: meta::PackedElement,
{
PackedArray::<T>::from_typed_array(self)
}
pub fn is_read_only(&self) -> bool {
self.as_inner().is_read_only()
}
fn balanced_ensure_mutable(&self) {
sys::balanced_assert!(
!self.is_read_only(),
"mutating operation on read-only array"
);
}
fn check_bounds(&self, index: usize) {
let len = self.len();
assert!(
index < len,
"Array index {index} is out of bounds: length is {len}",
);
}
fn ptr(&self, index: usize) -> sys::GDExtensionConstVariantPtr {
let ptr = self.ptr_or_null(index);
assert!(
!ptr.is_null(),
"Array index {index} out of bounds (len {len})",
len = self.len(),
);
ptr
}
fn ptr_or_null(&self, index: usize) -> sys::GDExtensionConstVariantPtr {
let index = to_i64(index);
let variant_ptr = unsafe { interface_fn!(array_operator_index_const)(self.sys(), index) };
sys::SysPtr::as_const(variant_ptr)
}
fn ptr_mut(&mut self, index: usize) -> sys::GDExtensionVariantPtr {
let ptr = self.ptr_mut_or_null(index);
assert!(
!ptr.is_null(),
"Array index {index} out of bounds (len {len})",
len = self.len(),
);
ptr
}
fn ptr_mut_or_null(&mut self, index: usize) -> sys::GDExtensionVariantPtr {
let index = to_i64(index);
unsafe { interface_fn!(array_operator_index)(self.sys_mut(), index) }
}
#[doc(hidden)]
pub unsafe fn as_inner_mut(&self) -> inner::InnerArray<'_> {
inner::InnerArray::from_outer_typed(self)
}
pub(super) fn as_inner(&self) -> ImmutableInnerArray<'_> {
ImmutableInnerArray {
inner: unsafe { self.as_inner_mut() },
}
}
#[cfg(safeguards_strict)] #[cfg_attr(published_docs, doc(cfg(safeguards_strict)))]
unsafe fn assume_type_ref<U: Element>(&self) -> &Array<U> {
unsafe { std::mem::transmute::<&Array<T>, &Array<U>>(self) }
}
fn as_any_ref(&self) -> &AnyArray {
unsafe { std::mem::transmute::<&Array<T>, &AnyArray>(self) }
}
fn as_any_mut(&mut self) -> &mut AnyArray {
unsafe { std::mem::transmute::<&mut Array<T>, &mut AnyArray>(self) }
}
pub(super) unsafe fn assume_type<U: Element>(self) -> Array<U> {
let result = Array::<U> {
opaque: self.opaque,
_phantom: PhantomData,
cached_element_type: OnceCell::new(),
};
ElementType::transfer_cache(&self.cached_element_type, &result.cached_element_type);
std::mem::forget(self);
result
}
#[cfg(safeguards_strict)] #[cfg_attr(published_docs, doc(cfg(safeguards_strict)))]
pub(crate) fn debug_validate_int_elements(&self) -> Result<(), ConvertError> {
let canonical_array = unsafe { self.assume_type_ref::<Variant>() };
for elem in canonical_array.iter_shared() {
elem.try_to::<T>().map_err(|_err| {
FromGodotError::BadArrayTypeInt {
expected_int_type: std::any::type_name::<T>(),
value: elem
.try_to::<i64>()
.expect("origin must be i64 compatible; this is a bug"),
}
.into_error(self.clone()) })?;
}
Ok(())
}
#[cfg(not(safeguards_strict))] #[cfg_attr(published_docs, doc(cfg(not(safeguards_strict))))]
pub(crate) fn debug_validate_int_elements(&self) -> Result<(), ConvertError> {
Ok(())
}
fn with_checked_type(self) -> Result<Self, ConvertError> {
let actual = self.element_type();
let expected = ElementType::of::<T>();
if actual.is_compatible_with(&expected) {
Ok(self)
} else {
let mismatch = ArrayMismatch { expected, actual };
Err(FromGodotError::BadArrayType(mismatch).into_error(self))
}
}
unsafe fn init_inner_type(&mut self) {
sys::strict_assert!(self.is_empty());
sys::strict_assert!(
self.cached_element_type.get().is_none(),
"init_inner_type() called twice"
);
let elem_ty = ElementType::of::<T>();
let _ = self.cached_element_type.set(elem_ty);
if elem_ty.is_typed() {
let script = Variant::nil();
#[allow(unused_assignments)]
let mut empty_string_name = None;
let class_name = if let Some(class_id) = elem_ty.class_id() {
class_id.string_sys()
} else {
empty_string_name = Some(StringName::default());
empty_string_name.as_ref().unwrap().string_sys()
};
unsafe {
interface_fn!(array_set_typed)(
self.sys_mut(),
elem_ty.variant_type().sys(),
class_name, script.var_sys(),
);
}
}
}
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!(array_construct_default);
ctor(self_ptr, std::ptr::null_mut());
})
};
init_fn(result.sys_mut());
result
}
pub(super) unsafe fn unchecked_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
let dest_type = Self::VARIANT_TYPE.variant_as_nil();
if variant.get_type() != dest_type {
return Err(FromVariantError::BadType {
expected: dest_type,
actual: variant.get_type(),
}
.into_error(variant.clone()));
}
let array = unsafe {
Self::new_with_uninit(|self_ptr| {
let array_from_variant = sys::builtin_fn!(array_from_variant);
array_from_variant(self_ptr, sys::SysPtr::force_mut(variant.var_sys()));
})
};
Ok(array)
}
pub(super) unsafe fn clone_unchecked(&self) -> Self {
let result = unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(array_construct_copy);
let args = [self.sys()];
ctor(self_ptr, args.as_ptr());
})
};
result.with_cache(self)
}
fn has_variant_t() -> bool {
element_variant_type::<T>() == VariantType::NIL
}
fn with_cache(self, source: &Self) -> Self {
ElementType::transfer_cache(&source.cached_element_type, &self.cached_element_type);
self
}
}
impl VarArray {
pub(crate) unsafe fn from_variant_unchecked(variant: &Variant) -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
let array_from_variant = sys::builtin_fn!(array_from_variant);
array_from_variant(self_ptr, sys::SysPtr::force_mut(variant.var_sys()));
})
}
}
}
unsafe impl<T: Element> GodotFfi for Array<T> {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(VariantType::ARRAY);
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 Element for VarArray {}
impl<T: Element> GodotConvert for Array<T> {
type Via = Self;
fn godot_shape() -> GodotShape {
if Self::has_variant_t() {
return GodotShape::Builtin {
variant_type: VariantType::ARRAY,
metadata: ParamMetadata::NONE,
};
}
GodotShape::TypedArray {
element: GodotElementShape::new(T::godot_shape()),
}
}
}
impl<T: Element> ToGodot for Array<T> {
type Pass = meta::ByRef;
fn to_godot(&self) -> &Self::Via {
self
}
fn to_godot_owned(&self) -> Self::Via {
unsafe { self.clone_unchecked() }
}
}
impl<T: Element> FromGodot for Array<T> {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
T::debug_validate_elements(&via)?;
Ok(via)
}
}
impl<T: Element> fmt::Debug for Array<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_variant().stringify())
}
}
impl<T: Element + fmt::Display> fmt::Display for Array<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (count, v) in self.iter_shared().enumerate() {
if count != 0 {
write!(f, ", ")?;
}
write!(f, "{v}")?;
}
write!(f, "]")
}
}
impl<T: Element> Clone for Array<T> {
fn clone(&self) -> Self {
let copy = unsafe { self.clone_unchecked() };
if cfg!(safeguards_strict) {
copy.with_checked_type()
.expect("copied array should have same type as original array")
} else {
copy
}
}
}
impl<T: Element> Var for Array<T> {
type PubType = Self;
fn var_get(field: &Self) -> Self::Via {
field.to_godot_owned()
}
fn var_set(field: &mut Self, value: Self::Via) {
*field = FromGodot::from_godot(value);
}
fn var_pub_get(field: &Self) -> Self::PubType {
field.clone()
}
fn var_pub_set(field: &mut Self, value: Self::PubType) {
*field = value;
}
}
impl<T> Export for Array<T> where T: Element + Export {}
impl<T: Element> BuiltinExport for Array<T> {}
impl<T> Export for Array<Gd<T>>
where
T: GodotClass + Bounds<Exportable = bounds::Yes>,
{
#[doc(hidden)]
fn as_node_class() -> Option<ClassId> {
PropertyHintInfo::object_as_node_class::<T>()
}
}
impl<T: GodotClass, D> Export for Array<DynGd<T, D>>
where
T: GodotClass + Bounds<Exportable = bounds::Yes>,
D: ?Sized + 'static,
{
#[doc(hidden)]
fn as_node_class() -> Option<ClassId> {
PropertyHintInfo::object_as_node_class::<T>()
}
}
impl<T: Element> Default for Array<T> {
#[inline]
fn default() -> Self {
let mut array = unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(array_construct_default);
ctor(self_ptr, std::ptr::null_mut())
})
};
unsafe { array.init_inner_type() };
array
}
}
impl<T: Element> Drop for Array<T> {
#[inline]
fn drop(&mut self) {
unsafe {
let array_destroy = sys::builtin_fn!(array_destroy);
array_destroy(self.sys_mut());
}
}
}
impl<T: Element> GodotType for Array<T> {
type Ffi = Self;
type ToFfi<'f>
= RefArg<'f, Array<T>>
where
Self: 'f;
fn to_ffi(&self) -> Self::ToFfi<'_> {
RefArg::new(self)
}
fn into_ffi(self) -> Self::Ffi {
self
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, ConvertError> {
Ok(ffi)
}
}
impl<T: Element> GodotFfiVariant for Array<T> {
fn ffi_to_variant(&self) -> Variant {
unsafe {
Variant::new_with_var_uninit(|variant_ptr| {
let array_to_variant = sys::builtin_fn!(array_to_variant);
array_to_variant(variant_ptr, sys::SysPtr::force_mut(self.sys()));
})
}
}
fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
let array = unsafe { Self::unchecked_from_variant(variant) }?;
array.with_checked_type()
}
}
impl<T: Element + ToGodot, const N: usize> From<&[T; N]> for Array<T> {
fn from(arr: &[T; N]) -> Self {
Self::from(&arr[..])
}
}
impl<T: Element + ToGodot> From<&[T]> for Array<T> {
fn from(slice: &[T]) -> Self {
let mut array = Self::new();
let len = slice.len();
if len == 0 {
return array;
}
unsafe { array.as_inner_mut() }.resize(to_i64(len));
let elements = unsafe { Variant::borrow_slice_mut(array.ptr_mut(0), len) };
for (element, array_slot) in slice.iter().zip(elements.iter_mut()) {
*array_slot = element.to_variant();
}
array
}
}
impl<T: Element + ToGodot> FromIterator<T> for Array<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut array = Self::new();
array.extend(iter);
array
}
}
impl<T: Element> Extend<T> for Array<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for item in iter.into_iter() {
self.push(meta::owned_into_arg(item));
}
}
}
impl<T: Element + FromGodot> From<&Array<T>> for Vec<T> {
fn from(array: &Array<T>) -> Vec<T> {
let len = array.len();
let mut vec = Vec::with_capacity(len);
let elements = unsafe { Variant::borrow_slice(array.ptr(0), len) };
vec.extend(elements.iter().map(T::from_variant));
vec
}
}
pub struct ArrayIter<'a, T: Element> {
array: &'a Array<T>,
next_idx: usize,
}
impl<T: Element + FromGodot> Iterator for ArrayIter<'_, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.next_idx < self.array.len() {
let idx = self.next_idx;
self.next_idx += 1;
let element_ptr = self.array.ptr_or_null(idx);
let variant = unsafe { Variant::borrow_var_sys(element_ptr) };
let element = T::from_variant(variant);
Some(element)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.array.len() - self.next_idx;
(remaining, Some(remaining))
}
}
impl<T: Element> PartialEq for Array<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
unsafe {
let mut result = false;
sys::builtin_call! {
array_operator_equal(self.sys(), other.sys(), result.sys_mut())
}
result
}
}
}
impl<T: Element> PartialOrd for Array<T> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
let op_less = |lhs, rhs| unsafe {
let mut result = false;
sys::builtin_call! {
array_operator_less(lhs, rhs, result.sys_mut())
}
result
};
if op_less(self.sys(), other.sys()) {
Some(std::cmp::Ordering::Less)
} else if op_less(other.sys(), self.sys()) {
Some(std::cmp::Ordering::Greater)
} else if self.eq(other) {
Some(std::cmp::Ordering::Equal)
} else {
None
}
}
}
#[macro_export]
macro_rules! array {
($($elements:expr_2021),* $(,)?) => {
{
let mut array = $crate::builtin::Array::default();
$(array.push($elements);)*
array
}
};
}
#[macro_export]
macro_rules! iarray {
($($elements:expr_2021),* $(,)?) => {
{
let mut array = $crate::builtin::Array::default();
$(array.__macro_push_direct($elements);)*
array
}
};
}
#[macro_export]
macro_rules! varray {
($($elements:expr_2021),* $(,)?) => {
{
let mut array = $crate::builtin::VarArray::default();
$(
array.push($elements);
)*
array
} as $crate::builtin::VarArray
};
}
#[macro_export]
macro_rules! vslice {
($($elements:expr_2021),* $(,)?) => {
&[$( $crate::meta::ToGodot::to_variant(&$elements), )*]
};
}
#[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
mod serialize {
use std::marker::PhantomData;
use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
impl<T> Serialize for Array<T>
where
T: Element + Serialize,
{
#[inline]
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut sequence = serializer.serialize_seq(Some(self.len()))?;
for e in self.iter_shared() {
sequence.serialize_element(&e)?
}
sequence.end()
}
}
impl<'de, T> Deserialize<'de> for Array<T>
where
T: Element + Deserialize<'de>,
{
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct ArrayVisitor<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for ArrayVisitor<T>
where
T: Element + Deserialize<'de>,
{
type Value = Array<T>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(std::any::type_name::<Self::Value>())
}
fn visit_seq<A>(
self,
mut seq: A,
) -> Result<Self::Value, <A as SeqAccess<'de>>::Error>
where
A: SeqAccess<'de>,
{
let mut vec = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
while let Some(val) = seq.next_element::<T>()? {
vec.push(val);
}
Ok(Self::Value::from(vec.as_slice()))
}
}
deserializer.deserialize_seq(ArrayVisitor::<T>(PhantomData))
}
}
}
fn __cannot_downcast_from_concrete() {}
#[test]
fn correct_variant_t() {
assert!(Array::<Variant>::has_variant_t());
assert!(!Array::<i64>::has_variant_t());
}