69 lines
1.9 KiB
Rust
69 lines
1.9 KiB
Rust
use crate::model::*;
|
|
|
|
mod query_filter;
|
|
pub use self::query_filter::QueryFilterSyntaxError;
|
|
use self::query_filter::*;
|
|
|
|
pub struct Query {
|
|
filters: Vec<QueryFilter>,
|
|
}
|
|
|
|
impl Query {
|
|
pub fn search(&self, input: &mut Iterator<Item = &Texture>) -> Vec<Texture> {
|
|
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::<Vec<Texture>>()
|
|
}
|
|
|
|
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<Query, QueryFilterSyntaxError> {
|
|
let mut result = Query { filters: vec![] };
|
|
|
|
for fltr in input {
|
|
let qryfltr = fltr.parse::<QueryFilter>()?;
|
|
result.filters.push(qryfltr);
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
}
|