use rust_stemmers::Stemmer;
use crate::language::Language;
use crate::normalize::normalize;
use crate::term::Term;
use crate::tokenize::tokenize;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StopWordPolicy {
DefaultForLanguage,
None,
}
impl StopWordPolicy {
fn removes(self, language: Language, token: &str) -> bool {
match self {
Self::DefaultForLanguage => language.stop_words().contains(&token),
Self::None => false,
}
}
}
#[derive(Clone, Debug)]
pub struct Analyzer {
default_language: Language,
stop_words: StopWordPolicy,
stemming: bool,
}
impl Analyzer {
#[must_use]
pub fn new() -> Self {
Self {
default_language: Language::English,
stop_words: StopWordPolicy::DefaultForLanguage,
stemming: false,
}
}
#[must_use]
pub fn builder() -> AnalyzerBuilder {
AnalyzerBuilder::new()
}
#[must_use]
pub fn default_language(&self) -> Language {
self.default_language
}
#[must_use]
pub fn analyze(&self, text: &str, lang_tag: Option<&str>) -> Vec<Term> {
let language = lang_tag
.and_then(Language::from_tag)
.unwrap_or(self.default_language);
let normalized = normalize(text);
let stemmer = self
.stemming
.then(|| Stemmer::create(language.stemmer_algorithm()));
let mut terms = Vec::new();
for token in tokenize(&normalized) {
if self.stop_words.removes(language, token) {
continue;
}
let term = match &stemmer {
Some(stemmer) => Term::from_normalized(stemmer.stem(token).into_owned()),
None => Term::from_normalized(token),
};
terms.push(term);
}
terms
}
}
impl Default for Analyzer {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug)]
pub struct AnalyzerBuilder {
default_language: Language,
stop_words: StopWordPolicy,
stemming: bool,
}
impl AnalyzerBuilder {
fn new() -> Self {
Self {
default_language: Language::English,
stop_words: StopWordPolicy::DefaultForLanguage,
stemming: false,
}
}
#[must_use]
pub fn default_language(mut self, language: Language) -> Self {
self.default_language = language;
self
}
#[must_use]
pub fn stop_words(mut self, policy: StopWordPolicy) -> Self {
self.stop_words = policy;
self
}
#[must_use]
pub fn stemming(mut self, enabled: bool) -> Self {
self.stemming = enabled;
self
}
#[must_use]
pub fn build(self) -> Analyzer {
Analyzer {
default_language: self.default_language,
stop_words: self.stop_words,
stemming: self.stemming,
}
}
}
impl Default for AnalyzerBuilder {
fn default() -> Self {
Self::new()
}
}