Diffstat (limited to 'src/build/builders/core/array.rs')
| -rw-r--r-- | src/build/builders/core/array.rs | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/src/build/builders/core/array.rs b/src/build/builders/core/array.rs new file mode 100644 index 0000000..2b8edb3 --- /dev/null +++ b/src/build/builders/core/array.rs @@ -0,0 +1,128 @@ +use core::{mem::MaybeUninit, ops::ControlFlow}; + +use crate::{ + any_trait, + protocol::{ + visitor::{Sequence, SequenceScope, Status}, + ControlFlowFor, Effect, Ready, + }, +}; + +#[cfg(feature = "alloc")] +use crate::protocol::AsyncEffect; + +#[cfg(all(feature = "alloc", not(feature = "std")))] +use alloc::boxed::Box; + +impl<'ctx, T, const N: usize> crate::Build<'ctx> for [T; N] +where + T: crate::Build<'ctx>, +{ + type Builder = Builder<'ctx, T::Builder, N>; +} + +#[derive(Debug)] +pub enum ArrayError<E> { + Incomplete, + Item(usize, E), +} + +pub struct Builder<'ctx, B: crate::Builder<'ctx>, const N: usize> { + array: MaybeUninit<[B::Value; N]>, + index: usize, + item_err: Option<(usize, B::Error)>, +} + +impl<'ctx, B: crate::Builder<'ctx>, const N: usize> Default for Builder<'ctx, B, N> { + fn default() -> Self { + Self { + array: MaybeUninit::uninit(), + index: 0, + item_err: None, + } + } +} + +impl<'ctx, B: crate::Builder<'ctx>, const N: usize> crate::Builder<'ctx> for Builder<'ctx, B, N> { + type Error = ArrayError<B::Error>; + + type Value = [B::Value; N]; + + fn as_visitor(&mut self) -> &mut crate::protocol::Visitor<'_, 'ctx> { + self + } + + fn build(self) -> Result<Self::Value, Self::Error> { + if let Some((index, err)) = self.item_err { + return Err(ArrayError::Item(index, err)); + } + + if self.index < N { + Err(ArrayError::Incomplete) + } else { + Ok(unsafe { self.array.assume_init() }) + } + } +} + +#[cfg(not(feature = "alloc"))] +any_trait! { + impl['a, 'ctx, B: crate::Builder<'ctx>, const N: usize] Builder<'ctx, B, N> = [ + dyn Sequence<'ctx> + 'a, + ]; +} + +#[cfg(feature = "alloc")] +any_trait! { + impl['a, 'ctx, B: crate::Builder<'ctx>, const N: usize] Builder<'ctx, B, N> = [ + dyn Sequence<'ctx> + 'a, + dyn Sequence<'ctx, AsyncEffect> + 'a, + ]; +} + +impl<'ctx, B: crate::Builder<'ctx>, const N: usize, E: Effect> Sequence<'ctx, E> + for Builder<'ctx, B, N> +{ + #[inline] + fn visit<'a>(&'a mut self, scope: &'a mut dyn SequenceScope<'ctx, E>) -> ControlFlowFor<'a, E> { + E::wrap(async { + loop { + // Check if the array is full. + if self.index >= N { + return ControlFlow::Continue(()); + } + + // Try to build the next value. + let mut builder = B::default(); + match scope.next(builder.as_visitor()).await { + ControlFlow::Continue(Status::Done) => { + // The sequence is done so the builder wasn't given a value. + // We just throw away the empty builder. + return ControlFlow::Continue(()); + } + ControlFlow::Continue(Status::Continue) => match builder.build() { + Ok(value) => { + // Put the value in the array, and move to the next one. + unsafe { maybe_uninit_array(&mut self.array).get_unchecked_mut(self.index).write(value) }; + self.index += 1; + } + Err(err) => { + // Record the item error and return a stop signal. + self.item_err = Some((self.index, err)); + return ControlFlow::Break(()); + } + }, + ControlFlow::Break(()) => { + return ControlFlow::Break(()); + } + } + } + }) + } +} + +fn maybe_uninit_array<T, const N: usize>( + array: &mut MaybeUninit<[T; N]>, +) -> &mut [MaybeUninit<T>; N] { + unsafe { &mut *(array as *mut _ as *mut [MaybeUninit<T>; N]) } +} |