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}