use super::{super::super::super::std::collections::*, preference::*, selector::*, weight::*};
use std::{hash::*, iter::*, str::*};
#[derive(Clone, Debug)]
pub struct Preferences<SelectionT>(pub Vec<Preference<SelectionT>>);
impl<SelectionT> Preferences<SelectionT> {
pub fn sorted(mut self) -> Self {
self.0.sort_by(|a, b| b.weight.cmp(&a.weight));
self
}
pub fn parse(representations: &Vec<&str>) -> Self
where
SelectionT: Clone + Eq + FromStr,
{
let preferences: Vec<_> = representations
.iter()
.flat_map(|representation| representation.split(","))
.filter_map(move |format| {
let mut split = format.splitn(2, ';');
let selector = (split.next().expect("splitn not empty").trim()).parse().ok()?;
let weight = match split.next() {
Some(weight) => Weight::parse(weight.trim())?,
None => Weight::MAX,
};
Some(Preference::new(selector, weight))
})
.collect();
Self(preferences).sorted()
}
pub fn best<'context, 'allowances, 'selection>(
&'context self,
allowances: &'allowances [SelectionT],
) -> Option<&'selection SelectionT>
where
'context: 'selection,
'allowances: 'selection,
SelectionT: Hash + Eq,
{
if self.0.is_empty() {
None
} else {
let mut candidates = FastHashSet::<&SelectionT>::with_capacity(allowances.len());
for (index, preference) in self.0.iter().enumerate() {
let selections = preference.selector.select(allowances);
if !selections.is_empty() {
candidates.extend(selections);
if preference.selector.is_specific() {
candidates.extend(self.select_tied(index, preference.weight, allowances));
}
break;
}
}
if candidates.len() == 1 {
return Some(candidates.into_iter().next().expect("iter not empty"));
} else if !candidates.is_empty() {
for selection in allowances {
if candidates.contains(&selection) {
return Some(selection);
}
}
}
None
}
}
pub fn best_or_first<'context, 'allowances, 'selection>(
&'context self,
allowances: &'allowances [SelectionT],
) -> &'selection SelectionT
where
'context: 'selection,
'allowances: 'selection,
SelectionT: Hash + Eq,
{
assert!(allowances.len() > 0);
self.best(allowances).unwrap_or_else(|| &allowances[0])
}
fn select_tied<'context, 'allowances, 'selection>(
&'context self,
index: usize,
weight: Weight,
allowances: &'allowances [SelectionT],
) -> FastHashSet<&'selection SelectionT>
where
'context: 'selection,
'allowances: 'selection,
SelectionT: Hash + Eq,
{
let mut tied = FastHashSet::with_capacity(self.0.len() - index);
for preference in &self.0[index + 1..] {
if preference.weight != weight {
break;
}
tied.extend(preference.selector.select(allowances));
}
tied
}
}