choose_from/
selector.rs

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