1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
//! Build an array dynamically without heap allocations, deferring errors to a
//! single `build` callsite.
//!
//! ```
//! # use build_array::ArrayBuilder;
//! let arr: [u8; 3] = ArrayBuilder::new()
//! .push(1)
//! .push(2)
//! .push(3)
//! .build_exact()
//! .unwrap();
//!
//! assert_eq!(arr, [1, 2, 3]);
//! ```
//!
//! You can choose how to handle the wrong number of [`push`](ArrayBuilder::push)
//! calls:
//! - [`build_exact`](ArrayBuilder::build_exact).
//! - [`build_pad`](ArrayBuilder::build_pad).
//! - [`build_pad_truncate`](ArrayBuilder::build_pad_truncate).
//!
//! # Comparison with other libraries
//! - [`arrayvec`] requires you to handle over-provision at each call to [`try_push`](arrayvec::ArrayVec::try_push).
//! - [`array_builder`](https://docs.rs/array_builder/latest/array_builder/) will
//! [`panic!`] on over-provision.
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt;
use arrayvec::ArrayVec;
/// Shorthand for [`ArrayBuilder::new`].
///
/// ```
/// # let arr: [&str; 0] =
/// build_array::new()
/// .push("hello")
/// .build_pad_truncate("pad");
/// ```
pub const fn new<T, const N: usize>() -> ArrayBuilder<T, N> {
ArrayBuilder::new()
}
/// Build an array dynamically without heap allocations.
///
/// See [module documentation](mod@self) for more.
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ArrayBuilder<T, const N: usize> {
inner: arrayvec::ArrayVec<T, N>,
excess: usize,
}
impl<T, const N: usize> ArrayBuilder<T, N> {
/// Create a new, empty builder.
pub const fn new() -> Self {
Self {
inner: ArrayVec::new_const(),
excess: 0,
}
}
/// Insert an item into the builder.
///
/// If the builder is already full, the item is immediately dropped.
pub fn push(&mut self, item: T) -> &mut Self {
if self.inner.try_push(item).is_err() {
self.excess += 1
};
self
}
fn pad_with(&mut self, mut f: impl FnMut() -> T) {
for _ in 0..self.inner.remaining_capacity() {
self.inner.push(f())
}
}
/// Pad out the array, returning an [`Err`] if there were too many calls to [`Self::push`].
/// The builder remains unchanged in the [`Err`] case.
///
/// ```
/// # use build_array::ArrayBuilder;
/// let arr = ArrayBuilder::<_, 3>::new().push("first").build_pad("padding").unwrap();
/// assert_eq!(arr, ["first", "padding", "padding"]);
///
/// ArrayBuilder::<_, 1>::new().push("first").push("too many now!").build_pad("").unwrap_err();
/// ```
pub fn build_pad(&mut self, item: T) -> Result<[T; N], Error>
where
T: Clone,
{
if self.excess > 0 {
return Err(Error(ErrorInner::TooMany(self.excess)));
}
self.pad_with(|| item.clone());
match self.inner.take().into_inner() {
Ok(it) => Ok(it),
Err(_) => unreachable!("we've just padded"),
}
}
/// Pad out the array, ignoring if there were too many calls to [`Self::push`].
/// The builder is restored to an empty state.
///
/// ```
/// # use build_array::ArrayBuilder;
/// let arr = ArrayBuilder::<_, 3>::new().push("first").build_pad_truncate("padding");
/// assert_eq!(arr, ["first", "padding", "padding"]);
///
/// let arr =
/// ArrayBuilder::<_, 1>::new().push("first").push("too many now!").build_pad_truncate("");
/// assert_eq!(arr, ["first"]);
/// ```
pub fn build_pad_truncate(&mut self, item: T) -> [T; N]
where
T: Clone,
{
self.pad_with(|| item.clone());
self.excess = 0;
match self.inner.take().into_inner() {
Ok(it) => it,
Err(_) => unreachable!("we've just padded"),
}
}
/// Require exactly `N` calls to [`Self::push`].
/// The builder remains unchanged in the [`Err`] case.
/// ```
/// # use build_array::ArrayBuilder;
///
/// ArrayBuilder::<_, 2>::new().push("too few").build_exact().unwrap_err();
/// ArrayBuilder::<_, 2>::new().push("way").push("too").push("many").build_exact().unwrap_err();
/// ArrayBuilder::<_, 2>::new().push("just").push("right").build_exact().unwrap();
/// ```
pub fn build_exact(&mut self) -> Result<[T; N], Error> {
if self.inner.remaining_capacity() == 0 && self.excess == 0 {
match self.inner.take().into_inner() {
Ok(it) => Ok(it),
Err(_) => unreachable!("remaining capacity is zero"),
}
} else {
Err(Error(ErrorInner::WrongNumber {
expected: N,
actual: self.inner.len() + self.excess,
}))
}
}
/// Return the current collection of items in the array.
///
/// Does not include excess items.
pub fn as_slice(&self) -> &[T] {
self.inner.as_slice()
}
/// Return the current collection of items in the array.
///
/// Does not include excess items.
pub fn as_mut_slice(&mut self) -> &mut [T] {
self.inner.as_mut_slice()
}
}
impl<T, const N: usize> Extend<T> for ArrayBuilder<T, N> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for it in iter {
self.push(it);
}
}
}
impl<T, const N: usize> FromIterator<T> for ArrayBuilder<T, N> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut this = Self::new();
this.extend(iter);
this
}
}
/// Error when building an array from [`ArrayBuilder`].
#[derive(Debug, Clone)]
pub struct Error(ErrorInner);
#[derive(Debug, Clone)]
enum ErrorInner {
TooMany(usize),
WrongNumber { expected: usize, actual: usize },
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
ErrorInner::TooMany(excess) => {
f.write_fmt(format_args!("too many elements, excess: {}", excess))
}
ErrorInner::WrongNumber { expected, actual } => f.write_fmt(format_args!(
"wrong number of elements, expected {}, got {}",
expected, actual
)),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}