1use abi_stable::traits::IntoReprRust;
2use fuzzy_matcher::FuzzyMatcher;
3use fuzzy_matcher::skim::SkimMatcherV2;
4use gravel_ffi::{ArcDynHit, ScoredHit};
5use itertools::Itertools;
6use lazy_static::lazy_static;
7use std::cmp::Ordering;
8
9lazy_static! {
10 static ref MATCHER: SkimMatcherV2 = SkimMatcherV2::default();
11}
12
13pub fn to_unscored(hits: impl IntoIterator<Item = ArcDynHit>) -> Vec<ScoredHit> {
15 hits.into_iter()
16 .map(|hit| {
17 let score = hit.override_score().unwrap_or(0);
18 ScoredHit::from(hit, score)
19 })
20 .sorted_by(compare_hits)
21 .collect()
22}
23
24pub fn to_scored(hits: Vec<ArcDynHit>, query: &str) -> Vec<ScoredHit> {
27 hits.into_iter()
28 .filter_map(|h| scored(h, query))
29 .sorted_by(compare_hits)
30 .collect()
31}
32
33fn scored(hit: ArcDynHit, query: &str) -> Option<ScoredHit> {
34 let override_score = hit.override_score().into_rust();
35
36 let score = override_score.or_else(|| score(&hit, query))?;
37
38 Some(ScoredHit::from(hit, score))
39}
40
41fn score(hit: &ArcDynHit, query: &str) -> Option<u32> {
42 let title = hit.title().into_rust();
43 MATCHER.fuzzy_match(title, query).map(|s| s as u32)
44}
45
46fn compare_hits(a: &ScoredHit, b: &ScoredHit) -> Ordering {
47 match b.score.cmp(&a.score) {
48 Ordering::Equal => a.hit.title().cmp(&b.hit.title()),
49 ordering => ordering,
50 }
51}