Skip to content

Commit

Permalink
refactor: move word selector into a dedicated module
Browse files Browse the repository at this point in the history
Also renamed RawWordSelector -> AsciiSortedWordSelector to better
reflect what it does
  • Loading branch information
Samyak2 committed Feb 4, 2024
1 parent 32985cc commit e6da3dc
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 32 deletions.
9 changes: 6 additions & 3 deletions src/bin/randomword.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::path::PathBuf;

use toipe::textgen::{RawWordSelector, WordSelector};
use toipe::wordlists::OS_WORDLIST_PATH;
use toipe::{
word_selector::{ascii_raw::AsciiSortedWordSelector, WordSelector},
wordlists::OS_WORDLIST_PATH,
};

fn main() {
let mut word_selector = RawWordSelector::from_path(PathBuf::from(OS_WORDLIST_PATH)).unwrap();
let mut word_selector =
AsciiSortedWordSelector::from_path(PathBuf::from(OS_WORDLIST_PATH)).unwrap();

let word = word_selector.new_word().unwrap();
println!("{}", word);
Expand Down
14 changes: 6 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
//! Toipe provides an API to invoke it from another application or
//! library. This documentation describes the API and algorithms used
//! internally.
//!
//! See [`RawWordSelector`] if you're looking for the word selection
//! algorithm.
pub mod config;
pub mod results;
pub mod textgen;
pub mod tui;
pub mod word_selector;
pub mod wordlists;

use std::io::StdinLock;
Expand All @@ -24,8 +21,9 @@ use config::ToipeConfig;
use results::ToipeResults;
use termion::input::Keys;
use termion::{color, event::Key, input::TermRead};
use textgen::{RawWordSelector, WordSelector};
use tui::{Text, ToipeTui};
use word_selector::ascii_raw::AsciiSortedWordSelector;
use word_selector::WordSelector;
use wordlists::{BuiltInWordlist, OS_WORDLIST_PATH};

use anyhow::{Context, Result};
Expand Down Expand Up @@ -80,19 +78,19 @@ impl<'a> Toipe {
config.wordlist_file.clone()
{
Box::new(
RawWordSelector::from_path(PathBuf::from(wordlist_path.clone())).with_context(
AsciiSortedWordSelector::from_path(PathBuf::from(wordlist_path.clone())).with_context(
|| format!("reading the word list from given path '{}'", wordlist_path),
)?,
)
} else if let Some(word_list) = config.wordlist.contents() {
Box::new(
RawWordSelector::from_string(word_list.to_string()).with_context(|| {
AsciiSortedWordSelector::from_string(word_list.to_string()).with_context(|| {
format!("reading the built-in word list {:?}", config.wordlist)
})?,
)
} else if let BuiltInWordlist::OS = config.wordlist {
Box::new(
RawWordSelector::from_path(PathBuf::from(OS_WORDLIST_PATH)).with_context(|| {
AsciiSortedWordSelector::from_path(PathBuf::from(OS_WORDLIST_PATH)).with_context(|| {
format!(
"reading from the OS wordlist at path '{}'. See https://en.wikipedia.org/wiki/Words_(Unix) for more info on this file and how it can be installed.",
OS_WORDLIST_PATH
Expand Down
14 changes: 14 additions & 0 deletions src/word_selector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
pub mod ascii_raw;

use std::io;

/// Describes a thing that provides new words.
pub trait WordSelector {
/// Returns a new word.
fn new_word(&mut self) -> Result<String, io::Error>;

/// Returns a [`Vec`] containing `num_words` words.
fn new_words(&mut self, num_words: usize) -> Result<Vec<String>, io::Error> {
(0..num_words).map(|_| self.new_word()).collect()
}
}
33 changes: 12 additions & 21 deletions src/textgen.rs → src/word_selector/ascii_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use rand::Rng;
use bisection::bisect_right;
use rand::prelude::ThreadRng;

use super::WordSelector;

/// Efficient selector of words from a word list.
///
/// The word list is given by a BufReader.
Expand All @@ -31,7 +33,7 @@ use rand::prelude::ThreadRng;
///
/// ### Algorithm
///
/// During initialization, the [`RawWordSelector`] iterates through all
/// During initialization, the [`AsciiSortedWordSelector`] iterates through all
/// the words in the list and builds an index mapping each letter (of
/// the alphabet) to its byte position in the file and the cumulative
/// number of words present starting with it.
Expand All @@ -55,17 +57,17 @@ use rand::prelude::ThreadRng;
///
/// `O(1)` (only needs fixed length arrays).
#[derive(Debug)]
pub struct RawWordSelector<T> {
pub struct AsciiSortedWordSelector<T> {
reader: BufReader<T>,
letter_pos: [u64; 26],
letter_lines_sum: [u64; 27],
}

impl<T: Seek + io::Read> RawWordSelector<T> {
impl<T: Seek + io::Read> AsciiSortedWordSelector<T> {
/// Create from any arbitrary [`BufReader`].
///
/// Please ensure that assumptions defined at
/// [`RawWordSelector#assumptions`] are valid for the contents.
/// [`AsciiSortedWordSelector#assumptions`] are valid for the contents.
pub fn new(mut reader: BufReader<T>) -> Result<Self, io::Error> {
let mut letter_pos = [0u64; 26];
let mut letter_lines = [0u64; 26];
Expand Down Expand Up @@ -195,11 +197,11 @@ impl<T: Seek + io::Read> RawWordSelector<T> {
}
}

impl RawWordSelector<File> {
impl AsciiSortedWordSelector<File> {
/// Create from a file at a path given by a [`PathBuf`].
///
/// Please ensure that assumptions defined at
/// [`RawWordSelector#assumptions`] are valid for this file.
/// [`AsciiSortedWordSelector#assumptions`] are valid for this file.
pub fn from_path(word_list_path: PathBuf) -> Result<Self, io::Error> {
let file = File::open(word_list_path)?;

Expand All @@ -209,31 +211,20 @@ impl RawWordSelector<File> {
}
}

impl RawWordSelector<Cursor<String>> {
impl AsciiSortedWordSelector<Cursor<String>> {
/// Create from a String representing the word list file.
///
/// Please ensure that assumptions defined at
/// [`RawWordSelector#assumptions`] are valid for the contents.
/// [`AsciiSortedWordSelector#assumptions`] are valid for the contents.
pub fn from_string(word_list: String) -> Result<Self, io::Error> {
let cursor = Cursor::new(word_list);
let reader = BufReader::new(cursor);

RawWordSelector::new(reader)
}
}

/// Describes a thing that provides new words.
pub trait WordSelector {
/// Returns a new word.
fn new_word(&mut self) -> Result<String, io::Error>;

/// Returns a [`Vec`] containing `num_words` words.
fn new_words(&mut self, num_words: usize) -> Result<Vec<String>, io::Error> {
(0..num_words).map(|_| self.new_word()).collect()
AsciiSortedWordSelector::new(reader)
}
}

impl<T: Seek + io::Read> WordSelector for RawWordSelector<T> {
impl<T: Seek + io::Read> WordSelector for AsciiSortedWordSelector<T> {
fn new_word(&mut self) -> Result<String, io::Error> {
let mut rng = rand::thread_rng();

Expand Down

0 comments on commit e6da3dc

Please sign in to comment.