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}