build_array/
lib.rs

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