diff --git a/src/bin/randomword.rs b/src/bin/randomword.rs index ee27d53..86f0bc8 100644 --- a/src/bin/randomword.rs +++ b/src/bin/randomword.rs @@ -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); diff --git a/src/lib.rs b/src/lib.rs index 77458c5..d0b0d74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -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}; @@ -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 diff --git a/src/word_selector.rs b/src/word_selector.rs new file mode 100644 index 0000000..0478737 --- /dev/null +++ b/src/word_selector.rs @@ -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; + + /// Returns a [`Vec`] containing `num_words` words. + fn new_words(&mut self, num_words: usize) -> Result, io::Error> { + (0..num_words).map(|_| self.new_word()).collect() + } +} diff --git a/src/textgen.rs b/src/word_selector/ascii_raw.rs similarity index 88% rename from src/textgen.rs rename to src/word_selector/ascii_raw.rs index 385d9f6..50679fc 100644 --- a/src/textgen.rs +++ b/src/word_selector/ascii_raw.rs @@ -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. @@ -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. @@ -55,17 +57,17 @@ use rand::prelude::ThreadRng; /// /// `O(1)` (only needs fixed length arrays). #[derive(Debug)] -pub struct RawWordSelector { +pub struct AsciiSortedWordSelector { reader: BufReader, letter_pos: [u64; 26], letter_lines_sum: [u64; 27], } -impl RawWordSelector { +impl AsciiSortedWordSelector { /// 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) -> Result { let mut letter_pos = [0u64; 26]; let mut letter_lines = [0u64; 26]; @@ -195,11 +197,11 @@ impl RawWordSelector { } } -impl RawWordSelector { +impl AsciiSortedWordSelector { /// 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 { let file = File::open(word_list_path)?; @@ -209,31 +211,20 @@ impl RawWordSelector { } } -impl RawWordSelector> { +impl AsciiSortedWordSelector> { /// 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 { 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; - - /// Returns a [`Vec`] containing `num_words` words. - fn new_words(&mut self, num_words: usize) -> Result, io::Error> { - (0..num_words).map(|_| self.new_word()).collect() + AsciiSortedWordSelector::new(reader) } } -impl WordSelector for RawWordSelector { +impl WordSelector for AsciiSortedWordSelector { fn new_word(&mut self) -> Result { let mut rng = rand::thread_rng();