choose_from/fixed.rs
1use crate::{choice, Choice, Guard};
2
3/// Wraps a fixed number of choices and provides methods that guarantee selection from those choices,
4/// where N is the possible number of choices set at compile time.
5// probably wouldn't need this type with HKTs :(
6#[derive(Debug, Hash, PartialEq, Eq)]
7pub struct SelectorFixed<const N: usize, T> {
8 choices: [T; N],
9}
10
11impl<const N: usize, T> SelectorFixed<N, T> {
12 pub(crate) fn with_choices(choices: [T; N]) -> SelectorFixed<N, T> {
13 SelectorFixed { choices }
14 }
15
16 /// The function `chooser` is used to choose from our provided
17 /// choices by returning a K-selection of them. The values of these choices are then
18 /// returned by the function.
19 /// ```
20 /// use choose_from::select_from_fixed;
21 /// let choices = ["Hi", "how", "are ya?"];
22 ///
23 /// let chosen = select_from_fixed(choices).with(|[first, second, third]| {
24 /// // the provided choices allow inspection of the values
25 /// assert_eq!(*first, "Hi");
26 ///
27 /// // this is our selection
28 /// [first, third]
29 /// });
30 ///
31 /// assert_eq!(chosen, ["Hi", "are ya?"]);
32 /// ```
33 // we pass our possible choices to the function wrapped in Choice, which only allows
34 // inspection of the value, and it must return an array of size K back full
35 // of our choices. The values returned are GUARANTEED to only come from our original
36 // choices thanks to the Choice struct and guard
37 pub fn with<const K: usize, C>(self, chooser: C) -> [T; K]
38 where
39 C: FnOnce([Choice<'_, T>; N]) -> [Choice<'_, T>; K],
40 {
41 // here we use a guard to prevent the caller from "smuggling" a value out of the closure.
42 // This ensures that Choice values built from our given choices are only
43 // available within the closure (they can't escape), since Choice has no
44 // publicly accessible constructor.
45 let _guard = Guard;
46 let choices = self.into_choices(&_guard);
47
48 chooser(choices).map(Choice::into_inner)
49 // _guard is dropped when function returns, which means that no one
50 // has any Choice values anymore
51 }
52
53 /// Like [with](SelectorFixed::with), but for returning any number of chosen values. Use this when
54 /// you want to ensure some values come from the choices, but the amount of chosen values returned
55 /// doesn't matter
56 /// ```
57 /// use choose_from::select_from_fixed;
58 ///
59 /// let choices = ["Hi", "how", "are ya?"];
60 ///
61 /// let chosen = select_from_fixed(choices).any_with(|choices| {
62 /// choices.into_iter().step_by(2).collect()
63 /// });
64 ///
65 /// assert_eq!(chosen, ["Hi", "are ya?"]);
66 /// ```
67 pub fn any_with<C>(self, chooser: C) -> Vec<T>
68 where
69 C: FnOnce([Choice<'_, T>; N]) -> Vec<Choice<'_, T>>,
70 {
71 let _guard = Guard;
72 let choices = self.into_choices(&_guard);
73
74 choice::to_values(chooser(choices))
75 }
76
77 fn into_choices(self, _guard: &'_ Guard) -> [Choice<'_, T>; N] {
78 self.choices.map(|t| Choice::with_guard(t, _guard))
79 }
80}