use core::iter::FusedIterator;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::ops::Deref;
use fluent_result::into::IntoResult;
use tap::Pipe;
use super::array_index::ArrayIndex;
use crate::errors::capacity::{CapacityError, FixedCap, RemainingCap};
use crate::errors::types::SizeHint;
#[derive(Debug)]
pub struct PartialArray<T, const N: usize> {
array: [MaybeUninit<T>; N],
back: ArrayIndex<N>,
}
impl<T, const N: usize> PartialArray<T, N> {
#[must_use]
#[doc(hidden)]
pub const fn new() -> Self {
Self { array: [const { MaybeUninit::uninit() }; N], back: ArrayIndex::new() }
}
#[doc(hidden)]
pub fn try_push(&mut self, item: T) -> Result<&mut T, T> {
match self.back.try_post_inc() {
Some(idx) => self.array[idx].write(item).into_ok(),
None => Err(item),
}
}
}
#[doc(hidden)]
impl<T, const N: usize> Default for PartialArray<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> PartialArray<T, N> {
fn initialized(&self) -> &[MaybeUninit<T>] {
&self.array[..*self.back]
}
fn initialized_mut(&mut self) -> &mut [MaybeUninit<T>] {
&mut self.array[..*self.back]
}
}
impl<T, const N: usize> Deref for PartialArray<T, N> {
type Target = [T];
fn deref(&self) -> &[T] {
self.initialized().pipe_ref(|slice| unsafe { slice.assume_init_ref() })
}
}
impl<T, const N: usize> RemainingCap for PartialArray<T, N> {
fn remaining_cap(&self) -> SizeHint {
SizeHint::at_most(N - *self.back)
}
}
impl<T, const N: usize> FixedCap for PartialArray<T, N> {
const CAP: SizeHint = SizeHint::at_most(N);
}
impl<T, U, const N: usize> PartialEq<[U]> for PartialArray<T, N>
where
T: PartialEq<U>,
{
fn eq(&self, other: &[U]) -> bool {
self[..] == other[..]
}
}
impl<T, const N: usize> Drop for PartialArray<T, N> {
fn drop(&mut self) {
self.initialized_mut().pipe_ref_mut(|slice| unsafe { slice.assume_init_drop() });
}
}
#[derive(Debug, thiserror::Error)]
#[error("Not enough elements to fill the array: {error}")]
pub struct IntoArrayError<T, const N: usize> {
pub partial_array: PartialArray<T, N>,
#[source]
pub error: CapacityError<T>,
}
impl<T, const N: usize> IntoArrayError<T, N> {
fn new(partial_array: PartialArray<T, N>) -> Self {
CapacityError::underflow_of::<[T; N]>(*partial_array.back).pipe(|error| Self { partial_array, error })
}
}
impl<T, const N: usize> TryFrom<PartialArray<T, N>> for [T; N] {
type Error = IntoArrayError<T, N>;
fn try_from(partial_array: PartialArray<T, N>) -> Result<Self, Self::Error> {
match *partial_array.back == N {
false => IntoArrayError::new(partial_array).into_err(),
true => ManuallyDrop::new(partial_array) .array
.as_ptr()
.cast::<[T; N]>()
.pipe(|ptr| unsafe { core::ptr::read(ptr) })
.into_ok(),
}
}
}
impl<T, const N: usize> IntoIterator for PartialArray<T, N> {
type Item = T;
type IntoIter = Drain<T, N>;
fn into_iter(self) -> Self::IntoIter {
Drain { partial_array: ManuallyDrop::new(self), next: ArrayIndex::new() }
}
}
impl<'a, T, const N: usize> IntoIterator for &'a PartialArray<T, N> {
type Item = &'a T;
type IntoIter = core::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self[..].iter()
}
}
#[derive(Debug)]
pub struct Drain<T, const N: usize> {
partial_array: ManuallyDrop<PartialArray<T, N>>,
next: ArrayIndex<N>,
}
impl<T, const N: usize> Iterator for Drain<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
(self.next < self.partial_array.back).then(|| {
self.next
.try_post_inc()
.expect("index should be within bounds")
.pipe(|idx| &self.partial_array.array[idx])
.pipe(|item| unsafe { item.assume_init_read() })
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
SizeHint::exact(*self.partial_array.back - *self.next).into()
}
}
impl<T, const N: usize> ExactSizeIterator for Drain<T, N> {}
impl<T, const N: usize> FusedIterator for Drain<T, N> {}
impl<T, const N: usize> Drop for Drain<T, N> {
fn drop(&mut self) {
let back = *self.partial_array.back;
self.partial_array.array[*self.next..back]
.pipe_ref_mut(|slice| unsafe { slice.assume_init_drop() });
}
}