Diffstat (limited to 'src/build/builders/core/array.rs')
-rw-r--r--src/build/builders/core/array.rs128
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]) }
+}