use crate::memory_core::dream::normalize_keyword;
pub(super) const ID_TOKEN_BOOST: f32 = 0.3;
const MAX_ID_CHARS: usize = 3;
const MIN_ID_CHARS: usize = 2;
pub(super) fn is_id_token(token: &str) -> bool {
(MIN_ID_CHARS..=MAX_ID_CHARS).contains(&token.chars().count())
&& token.chars().any(char::is_numeric)
}
pub(super) fn query_id_tokens(query_tokens: &[String]) -> Vec<&str> {
query_tokens
.iter()
.map(String::as_str)
.filter(|t| is_id_token(t))
.collect()
}
const MIN_HOLDERS: usize = 2;
const HOLDER_SHARE: usize = 5;
pub(super) fn max_holders(candidates: usize) -> usize {
(candidates / HOLDER_SHARE).max(MIN_HOLDERS)
}
pub(super) fn rare_id_tokens<'a>(id_tokens: &[&'a str], contents: &[&str]) -> Vec<&'a str> {
let limit = max_holders(contents.len());
id_tokens
.iter()
.copied()
.filter(|id| {
let holders = contents
.iter()
.filter(|c| holds_any(c, &[*id]))
.take(limit + 1);
holders.count() <= limit
})
.collect()
}
fn holds_any(content: &str, ids: &[&str]) -> bool {
if ids.is_empty() {
return false;
}
content.split_whitespace().any(|raw| {
if raw.chars().filter(|c| c.is_alphanumeric()).count() > MAX_ID_CHARS {
return false;
}
let token = normalize_keyword(raw);
ids.contains(&token.as_str())
})
}
pub(super) fn id_token_boost(id_tokens: &[&str], content: &str) -> f32 {
if holds_any(content, id_tokens) {
ID_TOKEN_BOOST
} else {
0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn id_token_shape() {
for id in ["e1", "f0", "42", "v2", "e12", "q15", "k8s"] {
assert!(is_id_token(id), "{id} is an id");
}
for word in [
"pm", "pr", "ci", "go", "ui", "fe", "fix", "bug", "ruling", "x", "7", "e123",
] {
assert!(!is_id_token(word), "{word} is not an id");
}
}
#[test]
fn id_token_boost_matches_whole_normalized_tokens() {
assert_eq!(id_token_boost(&["42"], "see issue #42."), ID_TOKEN_BOOST);
assert_eq!(id_token_boost(&["e1"], "Ruling E1: hold"), ID_TOKEN_BOOST);
assert_eq!(id_token_boost(&["e1"], "ruling e12 and be1"), 0.0);
assert_eq!(id_token_boost(&[], "ruling e1"), 0.0);
}
#[test]
fn an_id_most_candidates_hold_is_not_rare() {
let mut contents = vec!["ruling e1: hold"; 1];
contents.extend(["ship v2 builds"; 4]);
contents.extend(["ruling e2: other"; 10]);
assert_eq!(max_holders(contents.len()), 3);
assert_eq!(rare_id_tokens(&["e1", "v2"], &contents), vec!["e1"]);
assert_eq!(max_holders(4), MIN_HOLDERS);
}
}