use std::fmt;
use godot_ffi as sys;
use sys::{GodotFfi, ffi_methods};
use crate::builtin::iter::ArrayFunctionalOps;
use crate::builtin::*;
use crate::meta;
use crate::meta::error::ConvertError;
use crate::meta::inspect::ElementType;
use crate::meta::shape::GodotShape;
use crate::meta::{Element, FromGodot, GodotConvert, GodotFfiVariant, GodotType, ToGodot};
#[derive(PartialEq, PartialOrd)]
#[repr(transparent)] pub struct AnyArray {
array: VarArray,
}
impl AnyArray {
pub(super) fn from_typed_or_untyped<T: Element>(array: Array<T>) -> Self {
let inner = unsafe { array.assume_type::<Variant>() };
Self { array: inner }
}
pub(crate) fn new_untyped() -> Self {
Self {
array: VarArray::default(),
}
}
fn from_opaque(opaque: sys::types::OpaqueArray) -> Self {
Self {
array: VarArray::from_opaque(opaque),
}
}
pub fn at(&self, index: usize) -> Variant {
self.array.at(index)
}
pub fn get(&self, index: usize) -> Option<Variant> {
self.array.get(index)
}
pub fn contains(&self, value: &Variant) -> bool {
self.array.contains(value)
}
pub fn count(&self, value: &Variant) -> usize {
self.array.count(value)
}
#[doc(alias = "size")]
pub fn len(&self) -> usize {
to_usize(self.array.as_inner().size())
}
pub fn is_empty(&self) -> bool {
self.array.as_inner().is_empty()
}
pub fn hash_u32(&self) -> u32 {
self.array
.as_inner()
.hash()
.try_into()
.expect("Godot hashes are uint32_t")
}
#[doc(alias = "first")]
pub fn front(&self) -> Option<Variant> {
self.array.front()
}
#[doc(alias = "last")]
pub fn back(&self) -> Option<Variant> {
self.array.back()
}
pub fn clear(&mut self) {
self.balanced_ensure_mutable();
unsafe { self.as_inner_mut() }.clear();
}
#[doc(alias = "pop_back")]
pub fn pop(&mut self) -> Option<Variant> {
self.array.pop()
}
pub fn pop_front(&mut self) -> Option<Variant> {
self.array.pop_front()
}
#[doc(alias = "pop_at")]
pub fn remove(&mut self, index: usize) -> Variant {
self.array.remove(index)
}
pub fn erase(&mut self, value: &Variant) {
self.array.erase(value)
}
#[doc(alias = "resize")]
pub fn shrink(&mut self, new_size: usize) -> bool {
self.balanced_ensure_mutable();
if new_size >= self.len() {
return false;
}
unsafe { self.as_inner_mut() }.resize(to_i64(new_size));
true
}
pub fn duplicate_shallow(&self) -> AnyArray {
self.array.as_inner().duplicate(false)
}
pub fn duplicate_deep(&self) -> Self {
self.array.as_inner().duplicate(true)
}
#[doc(alias = "slice")]
pub fn subarray_shallow(&self, range: impl meta::SignedRange, step: Option<i32>) -> Self {
self.subarray_impl(range, step, false)
}
#[doc(alias = "slice")]
pub fn subarray_deep(&self, range: impl meta::SignedRange, step: Option<i32>) -> Self {
self.subarray_impl(range, step, true)
}
fn subarray_impl(&self, range: impl meta::SignedRange, step: Option<i32>, deep: bool) -> Self {
let step = step.unwrap_or(1);
assert_ne!(step, 0, "subarray: step cannot be zero");
let (begin, end) = range.signed();
let end = end.unwrap_or(i32::MAX as i64);
self.array.as_inner().slice(begin, end, step as i64, deep)
}
pub fn iter_shared(&self) -> AnyArrayIter<'_> {
AnyArrayIter {
inner: self.array.iter_shared(),
}
}
pub fn min(&self) -> Option<Variant> {
self.array.min()
}
pub fn max(&self) -> Option<Variant> {
self.array.max()
}
pub fn pick_random(&self) -> Option<Variant> {
self.array.pick_random()
}
pub fn find(&self, value: &Variant, from: Option<usize>) -> Option<usize> {
self.array.find(value, from)
}
pub fn rfind(&self, value: &Variant, from: Option<usize>) -> Option<usize> {
self.array.rfind(value, from)
}
pub fn bsearch(&self, value: &Variant) -> usize {
self.array.bsearch(value)
}
pub fn reverse(&mut self) {
self.balanced_ensure_mutable();
unsafe { self.as_inner_mut() }.reverse();
}
#[doc(alias = "sort")]
pub fn sort_unstable(&mut self) {
self.balanced_ensure_mutable();
unsafe { self.as_inner_mut() }.sort();
}
#[doc(alias = "sort_custom")]
pub fn sort_unstable_custom(&mut self, func: &Callable) {
self.balanced_ensure_mutable();
unsafe { self.as_inner_mut() }.sort_custom(func);
}
pub fn shuffle(&mut self) {
self.balanced_ensure_mutable();
unsafe { self.as_inner_mut() }.shuffle();
}
pub fn functional_ops(&self) -> ArrayFunctionalOps<'_, Variant> {
self.array.functional_ops()
}
pub fn element_type(&self) -> ElementType {
ElementType::get_or_compute_cached(
&self.array.cached_element_type,
|| self.array.as_inner().get_typed_builtin(),
|| self.array.as_inner().get_typed_class_name(),
|| self.array.as_inner().get_typed_script(),
)
}
pub fn is_read_only(&self) -> bool {
self.array.as_inner().is_read_only()
}
fn balanced_ensure_mutable(&self) {
sys::balanced_assert!(
!self.is_read_only(),
"mutating operation on read-only array"
);
}
#[doc(hidden)]
pub unsafe fn as_inner_mut(&self) -> inner::InnerArray<'_> {
inner::InnerArray::from_outer_typed(&self.array)
}
pub fn try_cast_array<T: Element>(self) -> Result<Array<T>, Self> {
let from_type = self.array.element_type();
let to_type = ElementType::of::<T>();
if from_type == to_type {
let array = unsafe { self.array.assume_type::<T>() };
Ok(array)
} else {
Err(self)
}
}
pub fn try_cast_var_array(self) -> Result<VarArray, Self> {
self.try_cast_array::<Variant>()
}
pub(crate) fn cast_array<T: Element>(self) -> Array<T> {
let from_type = self.element_type();
self.try_cast_array::<T>().unwrap_or_else(|_| {
panic!(
"cast_array_or_panic: expected element type {:?}, got {:?}",
ElementType::of::<T>(),
from_type,
)
})
}
}
unsafe impl GodotFfi for AnyArray {
const VARIANT_TYPE: sys::ExtVariantType = sys::ExtVariantType::Concrete(VariantType::ARRAY);
unsafe fn new_with_init(init_fn: impl FnOnce(sys::GDExtensionTypePtr)) -> Self {
let array = VarArray::new_uncached_type(init_fn);
Self { array }
}
fn sys(&self) -> sys::GDExtensionConstTypePtr {
self.array.sys()
}
fn sys_mut(&mut self) -> sys::GDExtensionTypePtr {
self.array.sys_mut()
}
#[allow(unsafe_op_in_unsafe_fn)] unsafe fn move_return_ptr(self, dst: sys::GDExtensionTypePtr, call_type: sys::PtrcallType) {
self.array.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 AnyArray {
fn clone(&self) -> Self {
let inner = unsafe { VarArray::clone_unchecked(&self.array) };
Self { array: inner }
}
}
impl meta::sealed::Sealed for AnyArray {}
impl Element for AnyArray {}
impl GodotConvert for AnyArray {
type Via = Self;
fn godot_shape() -> GodotShape {
GodotShape::of_builtin::<Self>()
}
}
impl ToGodot for AnyArray {
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 FromGodot for AnyArray {
fn try_from_godot(via: Self::Via) -> Result<Self, ConvertError> {
Ok(via)
}
}
impl fmt::Debug for AnyArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.array.fmt(f)
}
}
impl fmt::Display for AnyArray {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.array.fmt(f)
}
}
impl GodotType for AnyArray {
type Ffi = Self;
type ToFfi<'f>
= meta::RefArg<'f, AnyArray>
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 AnyArray {
fn ffi_to_variant(&self) -> Variant {
VarArray::ffi_to_variant(&self.array)
}
fn ffi_from_variant(variant: &Variant) -> Result<Self, ConvertError> {
let result = unsafe { VarArray::unchecked_from_variant(variant) };
result.map(|inner| Self { array: inner })
}
}
pub struct AnyArrayIter<'a> {
inner: super::array::ArrayIter<'a, Variant>,
}
impl<'a> Iterator for AnyArrayIter<'a> {
type Item = Variant;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}