word loading from data files
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::Result;
|
||||
|
||||
use super::blocklist::Blocklist;
|
||||
use super::word::Word;
|
||||
|
||||
pub fn load_wordlist(path: &Path, blocklist: &Blocklist, min_length: usize) -> Result<Vec<Word>> {
|
||||
let content = std::fs::read_to_string(path)?;
|
||||
let mut words = Vec::new();
|
||||
|
||||
for line in content.lines() {
|
||||
let w = line.trim().to_uppercase();
|
||||
if w.len() < min_length {
|
||||
continue;
|
||||
}
|
||||
if !w.chars().all(|c| c.is_alphabetic()) {
|
||||
continue;
|
||||
}
|
||||
if blocklist.is_blocked(&w) {
|
||||
continue;
|
||||
}
|
||||
// frequency heuristic: shorter common words score higher
|
||||
let freq = base_frequency(&w);
|
||||
words.push(Word::new(w, freq));
|
||||
}
|
||||
|
||||
// sort by length then alphabetically for deterministic ordering
|
||||
words.sort_by(|a, b| a.length.cmp(&b.length).then(a.text.cmp(&b.text)));
|
||||
words.dedup_by(|a, b| a.text == b.text);
|
||||
|
||||
Ok(words)
|
||||
}
|
||||
|
||||
fn base_frequency(word: &str) -> f64 {
|
||||
// simple heuristic: common lengths (3-8) get a boost, very long words score lower
|
||||
// this is a placeholder until we have real frequency data
|
||||
let len = word.len();
|
||||
match len {
|
||||
3 => 0.7,
|
||||
4 => 0.75,
|
||||
5 => 0.8,
|
||||
6 => 0.75,
|
||||
7 => 0.65,
|
||||
8 => 0.55,
|
||||
9 => 0.45,
|
||||
10 => 0.35,
|
||||
_ if len > 10 => 0.2,
|
||||
_ => 0.5,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn load_enable() {
|
||||
let path = Path::new("../data/wordlists/enable.txt");
|
||||
if !path.exists() {
|
||||
return; // skip if wordlist not present
|
||||
}
|
||||
let bl = Blocklist::empty();
|
||||
let words = load_wordlist(path, &bl, 3).unwrap();
|
||||
assert!(words.len() > 100_000, "expected 100k+ words, got {}", words.len());
|
||||
// all words should be uppercase, >= 3 chars, alpha only
|
||||
for w in &words {
|
||||
assert!(w.length >= 3);
|
||||
assert!(w.text.chars().all(|c| c.is_alphabetic()), "bad word: {}", w.text);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filters_short_words() {
|
||||
let path = Path::new("../data/wordlists/enable.txt");
|
||||
if !path.exists() {
|
||||
return;
|
||||
}
|
||||
let bl = Blocklist::empty();
|
||||
let words = load_wordlist(path, &bl, 5).unwrap();
|
||||
for w in &words {
|
||||
assert!(w.length >= 5);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blocklist_filters() {
|
||||
let path = Path::new("../data/wordlists/enable.txt");
|
||||
if !path.exists() {
|
||||
return;
|
||||
}
|
||||
let bl = Blocklist::empty();
|
||||
let words = load_wordlist(path, &bl, 3).unwrap();
|
||||
assert!(words.iter().any(|w| w.text == "THE"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user