use std::iter::{Extend, FromIterator};
use std::marker::PhantomData;
use gdnative_impl_proc_macros::doc_variant_collection_safety;
use crate::private::get_api;
use crate::sys;
use crate::core_types::OwnedToVariant;
use crate::core_types::ToVariant;
use crate::core_types::Variant;
use crate::NewRef;
use crate::thread_access::*;
use std::fmt;
pub struct VariantArray<Access: ThreadAccess = Shared> {
sys: sys::godot_array,
_marker: PhantomData<Access>,
}
impl<Access: ThreadAccess> VariantArray<Access> {
#[inline]
pub fn set<T: OwnedToVariant>(&self, idx: i32, val: T) {
unsafe { (get_api().godot_array_set)(self.sys_mut(), idx, val.owned_to_variant().sys()) }
}
#[inline]
pub fn get(&self, idx: i32) -> Variant {
unsafe { Variant((get_api().godot_array_get)(self.sys(), idx)) }
}
#[inline]
pub unsafe fn get_ref(&self, idx: i32) -> &Variant {
Variant::cast_ref((get_api().godot_array_operator_index_const)(
self.sys(),
idx,
))
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn get_mut_ref(&self, idx: i32) -> &mut Variant {
Variant::cast_mut_ref((get_api().godot_array_operator_index)(self.sys_mut(), idx))
}
#[inline]
pub fn count<T: ToVariant>(&self, val: T) -> i32 {
unsafe { (get_api().godot_array_count)(self.sys(), val.to_variant().sys()) }
}
#[inline]
pub fn is_empty(&self) -> bool {
unsafe { (get_api().godot_array_empty)(self.sys()) }
}
#[inline]
pub fn len(&self) -> i32 {
unsafe { (get_api().godot_array_size)(self.sys()) }
}
#[inline]
pub fn find<T: ToVariant>(&self, what: T, from: i32) -> i32 {
unsafe { (get_api().godot_array_find)(self.sys(), what.to_variant().sys(), from) }
}
#[inline]
pub fn contains<T: ToVariant>(&self, what: T) -> bool {
unsafe { (get_api().godot_array_has)(self.sys(), what.to_variant().sys()) }
}
#[inline]
pub fn rfind<T: ToVariant>(&self, what: T, from: i32) -> i32 {
unsafe { (get_api().godot_array_rfind)(self.sys(), what.to_variant().sys(), from) }
}
#[inline]
pub fn find_last<T: ToVariant>(&self, what: T) -> i32 {
unsafe { (get_api().godot_array_find_last)(self.sys(), what.to_variant().sys()) }
}
#[inline]
pub fn invert(&self) {
unsafe { (get_api().godot_array_invert)(self.sys_mut()) }
}
#[inline]
pub fn hash(&self) -> i32 {
unsafe { (get_api().godot_array_hash)(self.sys()) }
}
#[inline]
pub fn sort(&self) {
unsafe { (get_api().godot_array_sort)(self.sys_mut()) }
}
#[inline]
pub fn duplicate(&self) -> VariantArray<Unique> {
unsafe {
let sys = (get_api().godot_array_duplicate)(self.sys(), false);
VariantArray::<Unique>::from_sys(sys)
}
}
#[inline]
pub fn iter(&self) -> Iter<'_, Access> {
self.into_iter()
}
#[doc(hidden)]
#[inline]
pub fn sys(&self) -> *const sys::godot_array {
&self.sys
}
#[doc(hidden)]
#[inline]
pub fn sys_mut(&self) -> *mut sys::godot_array {
&self.sys as *const _ as *mut _
}
#[doc(hidden)]
#[inline]
pub fn from_sys(sys: sys::godot_array) -> Self {
VariantArray {
sys,
_marker: PhantomData,
}
}
unsafe fn cast_access<A: ThreadAccess>(self) -> VariantArray<A> {
let sys = self.sys;
std::mem::forget(self);
VariantArray::from_sys(sys)
}
}
impl<Access: LocalThreadAccess> VariantArray<Access> {
#[inline]
pub fn clear(&self) {
unsafe {
(get_api().godot_array_clear)(self.sys_mut());
}
}
#[inline]
pub fn remove(&self, idx: i32) {
unsafe { (get_api().godot_array_remove)(self.sys_mut(), idx) }
}
#[inline]
pub fn erase<T: ToVariant>(&self, val: T) {
unsafe { (get_api().godot_array_erase)(self.sys_mut(), val.to_variant().sys()) }
}
#[inline]
pub fn resize(&self, size: i32) {
unsafe { (get_api().godot_array_resize)(self.sys_mut(), size) }
}
#[inline]
pub fn push<T: OwnedToVariant>(&self, val: T) {
unsafe {
(get_api().godot_array_push_back)(self.sys_mut(), val.owned_to_variant().sys());
}
}
#[inline]
pub fn pop(&self) -> Variant {
unsafe { Variant((get_api().godot_array_pop_back)(self.sys_mut())) }
}
#[inline]
pub fn push_front<T: OwnedToVariant>(&self, val: T) {
unsafe {
(get_api().godot_array_push_front)(self.sys_mut(), val.owned_to_variant().sys());
}
}
#[inline]
pub fn pop_front(&self) -> Variant {
unsafe { Variant((get_api().godot_array_pop_front)(self.sys_mut())) }
}
#[inline]
pub fn insert<T: OwnedToVariant>(&self, at: i32, val: T) {
unsafe { (get_api().godot_array_insert)(self.sys_mut(), at, val.owned_to_variant().sys()) }
}
}
impl<Access: NonUniqueThreadAccess> VariantArray<Access> {
#[inline]
pub unsafe fn assume_unique(self) -> VariantArray<Unique> {
self.cast_access()
}
}
impl VariantArray<Unique> {
#[inline]
pub fn new() -> Self {
unsafe {
let mut sys = sys::godot_array::default();
(get_api().godot_array_new)(&mut sys);
Self::from_sys(sys)
}
}
#[inline]
pub fn into_shared(self) -> VariantArray<Shared> {
unsafe { self.cast_access() }
}
#[inline]
pub fn into_thread_local(self) -> VariantArray<ThreadLocal> {
unsafe { self.cast_access() }
}
}
impl VariantArray<Shared> {
#[inline]
pub fn new_shared() -> Self {
VariantArray::<Unique>::new().into_shared()
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn clear(&self) {
(get_api().godot_array_clear)(self.sys_mut());
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn remove(&self, idx: i32) {
(get_api().godot_array_remove)(self.sys_mut(), idx)
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn erase<T: ToVariant>(&self, val: T) {
(get_api().godot_array_erase)(self.sys_mut(), val.to_variant().sys())
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn resize(&self, size: i32) {
(get_api().godot_array_resize)(self.sys_mut(), size)
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn push<T: OwnedToVariant>(&self, val: T) {
(get_api().godot_array_push_back)(self.sys_mut(), val.owned_to_variant().sys());
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn pop(&self) -> Variant {
Variant((get_api().godot_array_pop_back)(self.sys_mut()))
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn push_front<T: OwnedToVariant>(&self, val: T) {
(get_api().godot_array_push_front)(self.sys_mut(), val.owned_to_variant().sys());
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn pop_front(&self) -> Variant {
Variant((get_api().godot_array_pop_front)(self.sys_mut()))
}
#[doc_variant_collection_safety]
#[inline]
pub unsafe fn insert<T: OwnedToVariant>(&self, at: i32, val: T) {
(get_api().godot_array_insert)(self.sys_mut(), at, val.owned_to_variant().sys())
}
}
impl VariantArray<ThreadLocal> {
#[inline]
pub fn new_thread_local() -> Self {
VariantArray::<Unique>::new().into_thread_local()
}
}
impl Default for VariantArray<Unique> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Default for VariantArray<Shared> {
#[inline]
fn default() -> Self {
VariantArray::new_shared()
}
}
impl Default for VariantArray<ThreadLocal> {
#[inline]
fn default() -> Self {
VariantArray::new_thread_local()
}
}
impl<Access: NonUniqueThreadAccess> NewRef for VariantArray<Access> {
#[inline]
fn new_ref(&self) -> Self {
unsafe {
let mut result = Default::default();
(get_api().godot_array_new_copy)(&mut result, self.sys());
Self::from_sys(result)
}
}
}
impl<Access: ThreadAccess> Drop for VariantArray<Access> {
#[inline]
fn drop(&mut self) {
unsafe { (get_api().godot_array_destroy)(self.sys_mut()) }
}
}
impl<Access: ThreadAccess> fmt::Debug for VariantArray<Access> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
pub struct Iter<'a, Access: ThreadAccess> {
arr: &'a VariantArray<Access>,
range: std::ops::Range<i32>,
}
impl<'a, Access: ThreadAccess> Iterator for Iter<'a, Access> {
type Item = Variant;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|idx| self.arr.get(idx))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
#[inline]
fn last(self) -> Option<Self::Item> {
if !self.arr.is_empty() {
Some(self.arr.get(self.arr.len() - 1))
} else {
None
}
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
use std::convert::TryFrom;
let n = i32::try_from(n).ok()?;
if self.arr.len() > n {
Some(self.arr.get(n))
} else {
None
}
}
}
impl<'a, Access: ThreadAccess> IntoIterator for &'a VariantArray<Access> {
type Item = Variant;
type IntoIter = Iter<'a, Access>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
Iter {
range: 0..self.len(),
arr: self,
}
}
}
pub struct IntoIter {
arr: VariantArray<Unique>,
range: std::ops::Range<i32>,
}
impl Iterator for IntoIter {
type Item = Variant;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|idx| self.arr.get(idx))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
#[inline]
fn last(self) -> Option<Self::Item> {
if !self.arr.is_empty() {
Some(self.arr.get(self.arr.len() - 1))
} else {
None
}
}
#[inline]
fn nth(&mut self, n: usize) -> Option<Self::Item> {
use std::convert::TryFrom;
let n = i32::try_from(n).ok()?;
if self.arr.len() > n {
Some(self.arr.get(n))
} else {
None
}
}
}
impl IntoIterator for VariantArray<Unique> {
type Item = Variant;
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
IntoIter {
range: 0..self.len(),
arr: self,
}
}
}
impl<T: ToVariant> FromIterator<T> for VariantArray<Unique> {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut arr = Self::new();
arr.extend(iter);
arr
}
}
impl<T: ToVariant, Access: LocalThreadAccess> Extend<T> for VariantArray<Access> {
#[inline]
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for elem in iter {
self.push(&elem.to_variant());
}
}
}
godot_test!(test_array {
let foo = Variant::from_str("foo");
let bar = Variant::from_str("bar");
let nope = Variant::from_str("nope");
let array = VariantArray::new();
assert!(array.is_empty());
assert_eq!(array.len(), 0);
array.push(&foo);
array.push(&bar);
assert_eq!(array.len(), 2);
assert!(array.contains(&foo));
assert!(array.contains(&bar));
assert!(!array.contains(&nope));
array.set(0, &bar);
array.set(1, &foo);
assert_eq!(&array.get(0), &bar);
assert_eq!(&array.get(1), &foo);
array.pop();
array.pop();
let x = Variant::from_i64(42);
let y = Variant::from_i64(1337);
let z = Variant::from_i64(512);
array.insert(0, &x);
array.insert(0, &y);
array.push_front(&z);
array.push_front(&z);
assert_eq!(array.find(&y, 0), 2);
assert_eq!(array.find_last(&z), 1);
assert_eq!(array.find(&nope, 0), -1);
array.invert();
assert_eq!(&array.get(0), &x);
array.pop_front();
array.pop_front();
assert_eq!(&array.get(0), &z);
array.resize(0);
assert!(array.is_empty());
array.push(&foo);
array.push(&bar);
let array2 = array.duplicate();
assert!(array2.contains(&foo));
assert!(array2.contains(&bar));
assert!(!array2.contains(&nope));
let array3 = VariantArray::new();
array3.push(&Variant::from_i64(42));
array3.push(&Variant::from_i64(1337));
array3.push(&Variant::from_i64(512));
assert_eq!(
&[42, 1337, 512],
array3.iter().map(|v| v.try_to_i64().unwrap()).collect::<Vec<_>>().as_slice(),
);
});
godot_test!(
test_array_debug {
let arr = VariantArray::new();
arr.push(&Variant::from_str("hello world"));
arr.push(&Variant::from_bool(true));
arr.push(&Variant::from_i64(42));
assert_eq!(format!("{:?}", arr), "[GodotString(hello world), Bool(True), I64(42)]");
}
);