kutil_http/headers/preferences/
preferences.rs1use super::{preference::*, selector::*, weight::*};
2
3use {
4 kutil_std::collections::*,
5 std::{hash::*, iter::*, str::*},
6};
7
8#[derive(Clone, Debug)]
14pub struct Preferences<SelectionT>(pub Vec<Preference<SelectionT>>);
15
16impl<SelectionT> Preferences<SelectionT> {
17 pub fn sorted(mut self) -> Self {
19 self.0.sort_by(|a, b| b.weight.cmp(&a.weight));
20 self
21 }
22
23 pub fn parse(representations: &Vec<&str>) -> Self
25 where
26 SelectionT: Clone + Eq + FromStr,
27 {
28 let preferences: Vec<_> = representations
29 .iter()
30 .flat_map(|representation| representation.split(","))
31 .filter_map(move |format| {
32 let mut split = format.splitn(2, ';');
33
34 let selector = (split.next().expect("next").trim()).parse().ok()?;
35
36 let weight = match split.next() {
37 Some(weight) => Weight::parse(weight.trim())?,
38 None => Weight::MAX,
39 };
40
41 Some(Preference::new(selector, weight))
42 })
43 .collect();
44
45 Self(preferences).sorted()
46 }
47
48 pub fn best<'own>(&'own self, allowances: &'own [SelectionT]) -> Option<&'own SelectionT>
52 where
53 SelectionT: Hash + Eq,
54 {
55 if self.0.is_empty() {
56 None
57 } else {
58 let mut candidates = FastHashSet::<&SelectionT>::with_capacity(allowances.len());
59
60 for (index, preference) in self.0.iter().enumerate() {
61 let selections = preference.selector.select(allowances);
62
63 if !selections.is_empty() {
64 candidates.extend(selections);
65
66 if preference.selector.is_specific() {
67 candidates.extend(self.select_tied(index, preference.weight, allowances));
70 }
71
72 break;
73 }
74 }
75
76 if candidates.len() == 1 {
77 return Some(candidates.into_iter().next().expect("should be safe"));
79 } else if !candidates.is_empty() {
80 for selection in allowances {
82 if candidates.contains(&selection) {
83 return Some(selection);
84 }
85 }
86 }
87
88 None
89 }
90 }
91
92 pub fn best_or_first<'own>(&'own self, allowances: &'own [SelectionT]) -> &'own SelectionT
99 where
100 SelectionT: Hash + Eq,
101 {
102 assert!(allowances.len() > 0);
103 self.best(allowances).unwrap_or_else(|| &allowances[0])
104 }
105
106 fn select_tied<'own>(
108 &'own self,
109 index: usize,
110 weight: Weight,
111 allowances: &'own [SelectionT],
112 ) -> FastHashSet<&'own SelectionT>
113 where
114 SelectionT: Hash + Eq,
115 {
116 let mut tied = FastHashSet::with_capacity(self.0.len() - index);
117
118 for preference in &self.0[index + 1..] {
119 if preference.weight != weight {
120 break;
121 }
122
123 tied.extend(preference.selector.select(allowances));
124 }
125
126 tied
127 }
128}