use super::{FaceInfo, ID, Query, Stretch, Style, Weight};
#[inline(never)]
pub(super) fn find_best_match(candidates: &[(ID, &FaceInfo)], query: &Query) -> Option<ID> {
debug_assert!(!candidates.is_empty());
let mut matching_set: Vec<usize> = (0..candidates.len()).collect();
let matches = matching_set
.iter()
.any(|&index| candidates[index].1.stretch == query.stretch);
let matching_stretch = if matches {
query.stretch
} else if query.stretch <= Stretch::Normal {
let stretch = matching_set
.iter()
.filter(|&&index| candidates[index].1.stretch < query.stretch)
.min_by_key(|&&index| {
query.stretch.to_number() - candidates[index].1.stretch.to_number()
});
match stretch {
Some(&matching_index) => candidates[matching_index].1.stretch,
None => {
let matching_index = *matching_set.iter().min_by_key(|&&index| {
candidates[index].1.stretch.to_number() - query.stretch.to_number()
})?;
candidates[matching_index].1.stretch
},
}
} else {
let stretch = matching_set
.iter()
.filter(|&&index| candidates[index].1.stretch > query.stretch)
.min_by_key(|&&index| {
candidates[index].1.stretch.to_number() - query.stretch.to_number()
});
match stretch {
Some(&matching_index) => candidates[matching_index].1.stretch,
None => {
let matching_index = *matching_set.iter().min_by_key(|&&index| {
query.stretch.to_number() - candidates[index].1.stretch.to_number()
})?;
candidates[matching_index].1.stretch
},
}
};
matching_set.retain(|&index| candidates[index].1.stretch == matching_stretch);
let style_preference = match query.style {
Style::Italic => [Style::Italic, Style::Oblique, Style::Normal],
Style::Oblique => [Style::Oblique, Style::Italic, Style::Normal],
Style::Normal => [Style::Normal, Style::Oblique, Style::Italic],
};
let matching_style = *style_preference.iter().find(|&query_style| {
matching_set
.iter()
.any(|&index| candidates[index].1.style == *query_style)
})?;
matching_set.retain(|&index| candidates[index].1.style == matching_style);
let weight = query.weight.0;
let matching_weight = if matching_set
.iter()
.any(|&index| candidates[index].1.weight.0 == weight)
{
Weight(weight)
} else if (400..450).contains(&weight)
&& matching_set
.iter()
.any(|&index| candidates[index].1.weight.0 == 500)
{
Weight::MEDIUM
} else if (450..=500).contains(&weight)
&& matching_set
.iter()
.any(|&index| candidates[index].1.weight.0 == 400)
{
Weight::NORMAL
} else if weight <= 500 {
let idx = matching_set
.iter()
.filter(|&&index| candidates[index].1.weight.0 <= weight)
.min_by_key(|&&index| weight - candidates[index].1.weight.0);
match idx {
Some(&matching_index) => candidates[matching_index].1.weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| candidates[index].1.weight.0 - weight)?;
candidates[matching_index].1.weight
},
}
} else {
let idx = matching_set
.iter()
.filter(|&&index| candidates[index].1.weight.0 >= weight)
.min_by_key(|&&index| candidates[index].1.weight.0 - weight);
match idx {
Some(&matching_index) => candidates[matching_index].1.weight,
None => {
let matching_index = *matching_set
.iter()
.min_by_key(|&&index| weight - candidates[index].1.weight.0)?;
candidates[matching_index].1.weight
},
}
};
matching_set.retain(|&index| candidates[index].1.weight == matching_weight);
matching_set
.into_iter()
.next()
.map(|index| candidates[index].0)
}