pacenotes_installer\choices/
configuration.rs1#[derive(Clone, Debug, Default, PartialEq, Eq)]
2pub struct Configuration {
3 pub lists: Vec<super::ChoiceList>,
5 pub extras: Vec<String>,
7}
8
9impl Configuration {
10 pub fn from_tree(
16 tree: &crate::tree::Tree,
17 keep: &[String],
18 choice_categories: &[&'static str],
19 ) -> Configuration {
20 let checked = tree.checked_leaves();
21 let lists = choice_categories
22 .iter()
23 .map(|&category| {
24 let options: Vec<super::ChoiceOption> = checked
25 .iter()
26 .copied()
27 .filter(|&leaf| tree.category_of(leaf) == category)
28 .filter_map(|leaf| {
29 let key = tree.node(leaf).key.clone()?;
30 Some(super::ChoiceOption {
31 key,
32 label: option_label(&tree.path_of(leaf)),
33 })
34 })
35 .collect();
36 let installed = checked
39 .iter()
40 .copied()
41 .filter(|&leaf| tree.category_of(leaf) == category)
42 .find(|&leaf| tree.node(leaf).installed)
43 .and_then(|leaf| tree.node(leaf).key.clone());
44 let position_of =
45 |key: &String| options.iter().position(|option| &option.key == key);
46 let selected = keep
47 .iter()
48 .find_map(position_of)
49 .or_else(|| installed.as_ref().and_then(position_of))
50 .or(if options.len() == 1 { Some(0) } else { None });
51 super::ChoiceList {
52 category,
53 options,
54 selected,
55 }
56 })
57 .collect();
58
59 let extras = checked
60 .iter()
61 .copied()
62 .filter(|&leaf| !choice_categories.contains(&tree.category_of(leaf)))
63 .filter_map(|leaf| tree.node(leaf).key.clone())
64 .collect();
65
66 Configuration { lists, extras }
67 }
68
69 pub fn is_complete(&self) -> bool {
70 self.lists.iter().all(super::ChoiceList::is_satisfied)
71 }
72
73 pub fn missing(&self) -> Vec<&'static str> {
75 self.lists
76 .iter()
77 .filter(|list| !list.is_satisfied())
78 .map(|list| list.category)
79 .collect()
80 }
81
82 pub fn select(&mut self, list: usize, option: Option<usize>) {
83 if let Some(list) = self.lists.get_mut(list) {
84 list.selected = option.filter(|&index| index < list.options.len());
85 }
86 }
87
88 pub fn selected_key(&self, category: &str) -> Option<&str> {
90 self.lists
91 .iter()
92 .find(|list| list.category == category)
93 .and_then(super::ChoiceList::selected_key)
94 }
95
96 pub fn selected_keys(&self) -> Vec<String> {
98 self.lists
99 .iter()
100 .filter_map(|list| list.selected_key().map(str::to_string))
101 .collect()
102 }
103
104 pub fn picks_by_category(&self) -> std::collections::BTreeMap<String, String> {
106 self.lists
107 .iter()
108 .filter_map(|list| {
109 let key = list.selected_key()?;
110 Some((list.category.to_string(), key.to_string()))
111 })
112 .collect()
113 }
114
115 pub fn install_keys(&self) -> Vec<String> {
117 let mut keys = self.selected_keys();
118 for extra in &self.extras {
119 if !keys.contains(extra) {
120 keys.push(extra.clone());
121 }
122 }
123 keys
124 }
125}
126
127fn option_label(path: &[&str]) -> String {
130 match path {
131 [_category, below @ ..] if !below.is_empty() => below.join(" / "),
132 _ => path.join(" / "),
133 }
134}