use crate::frontier::seed::Seed;
pub trait Score: Send + Sync + 'static {
fn score(&self, seed: &Seed) -> i32;
}
impl<F> Score for F
where
F: Fn(&Seed) -> i32 + Send + Sync + 'static,
{
fn score(&self, seed: &Seed) -> i32 {
self(seed)
}
}
#[derive(Debug, Clone)]
pub struct Keywords {
words: Vec<String>,
weight: i32,
}
impl Keywords {
pub fn new<I>(words: I) -> Self
where
I: IntoIterator<Item: AsRef<str>>,
{
let words = words
.into_iter()
.map(|word| word.as_ref().to_lowercase())
.filter(|word| !word.is_empty())
.collect();
Self { words, weight: 1 }
}
#[must_use]
pub const fn with_weight(mut self, weight: i32) -> Self {
self.weight = weight;
self
}
#[must_use]
pub fn matches(&self, text: &str) -> i32 {
let text = text.to_lowercase();
let found = self
.words
.iter()
.filter(|word| text.contains(word.as_str()))
.count();
i32::try_from(found)
.unwrap_or(i32::MAX)
.saturating_mul(self.weight)
}
}
impl Score for Keywords {
fn score(&self, seed: &Seed) -> i32 {
let uri = seed.uri();
let path_and_query = uri.path_and_query().map_or("", |path| path.as_str());
self.matches(path_and_query)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ShallowFirst;
impl Score for ShallowFirst {
fn score(&self, seed: &Seed) -> i32 {
i32::try_from(seed.depth().get()).map_or(i32::MIN, |depth| -depth)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::crawler::Depth;
use crate::frontier::test_util::seed;
#[test]
fn keywords_count_case_insensitively_in_path_and_query() {
let keywords = Keywords::new(["Laptop", "review", ""]).with_weight(5);
assert_eq!(keywords.score(&seed("laptops/REVIEWS?q=1")), 10);
assert_eq!(keywords.score(&seed("search?q=laptop")), 5);
assert_eq!(keywords.score(&seed("phones")), 0);
assert_eq!(keywords.matches("A laptop"), 5);
}
#[test]
fn keywords_ignore_the_host() {
let keywords = Keywords::new(["example"]);
assert_eq!(keywords.score(&seed("page")), 0);
}
#[test]
fn shallow_first_scores_minus_the_depth() {
assert_eq!(ShallowFirst.score(&seed("root")), 0);
let deep = seed("deep").with_depth(Depth::ROOT.child().child());
assert_eq!(ShallowFirst.score(&deep), -2);
}
}