use crate::model::*; mod query_filter; pub use self::query_filter::QueryFilterSyntaxError; use self::query_filter::*; pub struct Query { filters: Vec, } impl Query { pub fn search(&self, input: &mut Iterator) -> Vec { let mut results: Vec<(u64, &Texture)> = Vec::new(); // We use pseudo decimal fixed point numbers here. // 1.000_000 = 1_000_000; // This is done, since algorithms like quicksort can fail on floats. let required_score = self.required_score() * 1_000_000u64; for texture in input { let mut score = 0u64; for (pos, filter) in self.filters.iter().enumerate() { match filter.score(texture) { Score::RequiredMatch(true) => (), Score::RequiredMatch(false) => { // skip this texture continue; } Score::Match(true) => { score += 1_000_000u64 * (1 + (0.1 / f64::sqrt(pos as f64 + 1.0)) as u64); } Score::Match(false) => (), } } if score >= required_score { results.push((score, texture)) } } results.sort_by_key(|(score, _)| *score); results .iter() .map(|(_, texture)| Texture::clone(texture)) .collect::>() } fn required_score(&self) -> u64 { let non_special = self.filters.iter().filter(|f| !f.is_special()).count() as u64; // ceil(non_special / 2) (non_special + 1) / 2 } pub fn parse(input: &[String]) -> Result { let mut result = Query { filters: vec![] }; for fltr in input { let qryfltr = fltr.parse::()?; result.filters.push(qryfltr); } Ok(result) } }