use crate::fuzzy::{self, FuzzyScore}; use crate::query::ParsedQuery; use serde::Deserialize; use std::cmp::max; #[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct Song { pub title: String, pub artist: String, pub song_hash: String, pub cover: Option, pub language: Option, pub video: Option, pub year: Option, pub genre: Option, pub bpm: String, #[serde(rename = "duetsingerp1")] pub duet_singer_1: Option, #[serde(rename = "duetsingerp2")] pub duet_singer_2: Option, } impl Song { pub fn duet(&self) -> Option<(&str, &str)> { self.duet_singer_1 .as_deref() .zip(self.duet_singer_2.as_deref()) } pub fn fuzzy_compare(&self, query: &ParsedQuery) -> FuzzyScore { let bad = || -1; let filter_strs = |query: Option<&str>, item: Option<&str>| { if let Some(query) = query { match item { Some(item) => { let score = fuzzy::compare(item.chars(), query.chars()); if score < fuzzy::max_score(query) / 2 { return false; } } None => return false, } } true }; let filter_bool = |query: Option, item| !matches!(query, Some(query) if query != item); let filters: &[&dyn Fn() -> bool] = &[ &|| filter_bool(query.duet, self.duet().is_some()), &|| filter_bool(query.video, self.video.is_some()), &|| filter_strs(query.language, self.language.as_deref()), &|| filter_strs(query.genre, self.genre.as_deref()), &|| filter_strs(query.year, self.year.as_deref()), ]; if !filters.iter().all(|f| f()) { return bad(); } let mut score = FuzzyScore::default(); if let Some(plain) = &query.plain { let title_score = fuzzy::compare(self.title.chars(), plain.chars()); let artist_score = fuzzy::compare(self.artist.chars(), plain.chars()); score = max(title_score, artist_score); } if let Some(title) = query.title { let new_score = fuzzy::compare(self.title.chars(), title.chars()); score = max(score, new_score); } if let Some(artist) = query.artist { let new_score = fuzzy::compare(self.artist.chars(), artist.chars()); score = max(score, new_score); } score } }