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 {}