collar/lib.rs
1#![no_std]
2//! provides [`collect_array`](CollectArray::collect_array) and [`try_from_fn`].
3//! allowing easy vec-free collection to an array.
4//!
5//! ```
6//! # /*
7//! use collar::*;
8//! let Some([ty, path, http]) = request.split(' ').collect_array_checked() else {
9//! return;
10//! };
11//! # */
12//! ```
13
14use core::{
15 mem::{ManuallyDrop as MD, MaybeUninit as MU, forget},
16 ptr::drop_in_place,
17};
18use error::Error;
19pub use error::Error as CollectorError;
20mod error;
21mod maybe;
22use maybe::Maybe;
23
24/// Collect to an array.
25pub trait CollectArray: Iterator + Sized {
26 /// Lets you collect an iterator into a fixed length array with no vec allocation.
27 /// Handle remainder as you wish. Does not consume the iterator.
28 ///
29 /// # Panics
30 ///
31 /// when <code>[next](Iterator::next)() is [None]</code> before the array is filled.
32 /// for a non panicking alternative, see [`collect_array_checked`](CollectArray::collect_array_checked).
33 ///
34 /// ```
35 /// use collar::*;
36 /// let array = (0usize..).map(|x| x * 2).collect_array();
37 /// // indexes are: 0 1 2 3 4 5 6 7
38 /// assert_eq!(array, [0, 2, 4, 6, 8, 10, 12, 14]);
39 /// ```
40 fn collect_array<const N: usize>(&mut self) -> [Self::Item; N] {
41 self.collect_array_checked()
42 .unwrap_or_else(|x| panic!("couldnt fill buffer of length {N} only had {x} elements"))
43 }
44 /// Non panicking version of [`collect_array`](CollectArray::collect_array).
45 ///
46 /// Lets you collect an iterator into a fixed length array with no vec allocation, with no panics.
47 /// If the iterator returns [`None`] at any point, returns <code>[Err]\(elements filed\)</code>.
48 ///
49 /// If you wish to simply populate the array with [`None`] if the iterator returns [`None`], use [`items`](CollectArray::items).
50 ///
51 /// ```
52 /// use collar::*;
53 /// let array: Result<[u8; 10], usize> = std::iter::repeat(5).take(3).collect_array_checked();
54 /// // does not fill array -> produces `Err`, with number of elements filled.
55 /// assert_eq!(array, Err(3));
56 /// ```
57 fn collect_array_checked<const N: usize>(&mut self) -> Result<[Self::Item; N], usize> {
58 try_from_fn(|elem| self.next().ok_or(elem))
59 }
60
61 /// Creates an array [T; N] where each fallible (i.e [`Option`] or [`Result`]) element is begotten from [`next`](Iterator::next).
62 /// Unlike [`collect_array`](CollectArray::collect_array), where the element creation can't fail, this version will return an error if any element creation was unsuccessful (returned [`Err`] or [`None`]).
63 /// In the case where the iterator ran out of elements, this returns a [`CollectorError`] containing the count.
64 ///
65 /// The return type of this function depends on the [`Item`](Iterator::Item) of this [`Iterator`].
66 /// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N], CollectorError<E>>`.
67 /// If you return `Option<T>` from the closure, you'll get an `Result<[T; N], CollectorError<()>>`.
68 /// ```
69 /// use collar::CollectArray;
70 /// let array: Result<[i8; 200], _> = (0..).map(|x| x.try_into()).try_collect_array();
71 /// assert_eq!(array.unwrap_err().at, 128);
72 ///
73 /// // note the ok(); the try trait is still unstable. (so this is a Result<_, ()>::ok)
74 /// let array: Option<[_; 4]> = (0usize..).map(|i| i.checked_add(100)).try_collect_array().ok();
75 /// assert_eq!(array, Some([100, 101, 102, 103]));
76 ///
77 /// let array: Option<[_; 4]> = (0usize..).map(|i| i.checked_sub(100)).try_collect_array().ok();
78 /// assert_eq!(array, None);
79 /// ```
80 fn try_collect_array<const N: usize>(
81 &mut self,
82 ) -> Result<[<Self::Item as Maybe>::Unwrap; N], Error<N, <Self::Item as Maybe>::Or>>
83 where
84 Self::Item: Maybe,
85 {
86 try_from_fn(|elem| {
87 self.next()
88 // no error, ran out
89 .ok_or(None)
90 // some error, flattened
91 .and_then(|x| x.asr().map_err(Some))
92 .map_err(|x| Error { error: x, at: elem })
93 })
94 }
95
96 /// This function fills an array with this iterators elements.
97 /// It will always return (unless the iterator panics).
98 /// ```
99 /// use collar::*;
100 /// assert_eq!(
101 /// (0..).items::<5>(),
102 /// (0..).map(Some).collect_array::<5>(),
103 /// )
104 /// ```
105 fn items<const N: usize>(&mut self) -> [Option<Self::Item>; N] {
106 from_fn(|_| self.next())
107 }
108}
109impl<I: Iterator> CollectArray for I {}
110
111struct OnDrop<F: FnOnce()> {
112 f: MD<F>,
113}
114impl<F: FnOnce()> OnDrop<F> {
115 fn guard(x: F) -> Self {
116 Self { f: MD::new(x) }
117 }
118}
119
120impl<F: FnOnce()> Drop for OnDrop<F> {
121 fn drop(&mut self) {
122 // SAFETY: `Drop::drop` is only called once.
123 unsafe { MD::take(&mut self.f)() }
124 }
125}
126
127const unsafe fn transmute_unchecked<T, U>(value: T) -> U {
128 const { assert!(size_of::<T>() == size_of::<U>()) }
129 #[repr(C)]
130 union Transmute<T, U> {
131 t: MD<T>,
132 u: MD<U>,
133 }
134 unsafe { MD::into_inner(Transmute { t: MD::new(value) }.u) }
135}
136
137/// [`std::array::try_from_fn`] on stable.
138///
139/// Creates an array `[T; N]` where each fallible array element `T` is returned by the `cb` call.
140/// Unlike [`from_fn`], where the element creation can't fail, this version will return an error
141/// if any element creation was unsuccessful.
142///
143/// The return type of this function depends on the return type of the closure.
144/// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N], E>`.
145/// If you return `Option<T>` from the closure, you'll get an `Result<[T; N], ()>`.
146///
147/// # Arguments
148///
149/// * `cb`: Callback where the passed argument is the current array index.
150///
151/// # Example
152///
153/// ```rust
154/// let array: Result<[u8; 5], _> = collar::try_from_fn(|i| i.try_into());
155/// assert_eq!(array, Ok([0, 1, 2, 3, 4]));
156///
157/// let array: Result<[i8; 200], _> = collar::try_from_fn(|i| i.try_into());
158/// assert!(array.is_err());
159///
160/// let array: Option<[_; 4]> = collar::try_from_fn(|i| i.checked_add(100)).ok();
161/// assert_eq!(array, Some([100, 101, 102, 103]));
162///
163/// let array: Option<[_; 4]> = collar::try_from_fn(|i| i.checked_sub(100)).ok();
164/// assert_eq!(array, None);
165/// ```
166pub fn try_from_fn<R: Maybe, const N: usize>(
167 mut x: impl FnMut(usize) -> R,
168) -> Result<[R::Unwrap; N], R::Or> {
169 let mut out = [const { MU::uninit() }; N];
170 // initialize each element of `out`
171 for elem in 0..N {
172 let guard = OnDrop::guard(|| unsafe {
173 let p = &raw mut out[..elem] as *mut [R::Unwrap];
174 let guard = OnDrop::guard(|| drop_in_place(p));
175 drop_in_place(p);
176 // dont drop! (again)
177 forget(guard);
178 });
179 let e = x(elem).asr()?;
180 // dont drop!
181 forget(guard);
182 out[elem] = MU::new(e);
183 }
184 // SAFETY: each element has been initialized
185 Ok(unsafe { transmute_unchecked(out) })
186}
187
188#[doc(no_inline)]
189pub use core::array::from_fn;