use std::array;
use std::mem;
use std::mem::MaybeUninit;
use std::ptr;

#[derive(Debug)]
pub(crate) struct ArrayNotFilled;

pub(crate) struct ArrayBuilder<T, const S: usize> {
    array: [MaybeUninit<T>; S],
    filled: usize,
}

impl<T, const S: usize> Drop for ArrayBuilder<T, S> {
    fn drop(&mut self) {
        for i in 0..self.filled {
            unsafe {
                self.array[i].assume_init_drop();
            }
        }
    }
}

impl<T, const S: usize> ArrayBuilder<T, S> {
    pub(crate) fn new() -> Self {
        ArrayBuilder {
            array: array::from_fn(|_| MaybeUninit::uninit()),
            filled: 0,
        }
    }

    pub(crate) fn push(&mut self, value: T) {
        assert!(self.filled < S);
        unsafe {
            self.array
                .as_mut_ptr()
                .cast::<T>()
                .add(self.filled)
                .write(value);
        }
        self.filled += 1;
    }

    fn try_finish(mut self) -> Result<[T; S], ArrayNotFilled> {
        if self.filled != S {
            unsafe {
                ptr::drop_in_place(&mut self.array[self.filled..S]);
            }
            return Err(ArrayNotFilled);
        }
        let array = unsafe { mem::transmute_copy::<[MaybeUninit<T>; S], [T; S]>(&self.array) };
        mem::forget(self);
        Ok(array)
    }

    pub(crate) fn finish(self) -> [T; S] {
        self.try_finish().unwrap()
    }
}
