Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,16 @@ pub struct ToipeConfig {
/// Number of words to show on each test.
#[clap(short, long, default_value_t = 30)]
pub num_words: usize,


/// Read full text sequentially
#[clap(short = 's', long = "sequential", conflicts_with = "wordlist")]
pub use_sequential_words: bool,

/// Whether to include punctuation
#[clap(short, long)]
pub punctuation: bool,

}

impl ToipeConfig {
Expand Down
15 changes: 11 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use config::ToipeConfig;
use results::ToipeResults;
use termion::input::Keys;
use termion::{color, event::Key, input::TermRead};
use textgen::{PunctuatedWordSelector, RawWordSelector, WordSelector};
use textgen::{PunctuatedWordSelector, RawWordSelector, SequentialFileWordSelector, WordSelector};
use tui::{Text, ToipeTui};
use wordlists::{BuiltInWordlist, OS_WORDLIST_PATH};

Expand Down Expand Up @@ -76,9 +76,16 @@ impl<'a> Toipe {
/// Initializes the word selector.
/// Also invokes [`Toipe::restart()`].
pub fn new(config: ToipeConfig) -> Result<Self> {
let mut word_selector: Box<dyn WordSelector> = if let Some(wordlist_path) =
config.wordlist_file.clone()
{
let word_selector: Box<dyn WordSelector> = if config.use_sequential_words {
if let Some(wordlist_path) = config.wordlist_file.clone() {
Box::new(
SequentialFileWordSelector::from_path(PathBuf::from(wordlist_path))
.with_context(|| format!("reading words from path"))?,
)
} else {
return Err(ToipeError::from("Undefined path.".to_owned()))?;
}
} else if let Some(wordlist_path) = config.wordlist_file.clone() {
Box::new(
RawWordSelector::from_path(PathBuf::from(wordlist_path.clone())).with_context(
|| format!("reading the word list from given path '{}'", wordlist_path),
Expand Down
33 changes: 33 additions & 0 deletions src/textgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,39 @@ impl<T: Seek + io::Read> WordSelector for RawWordSelector<T> {
}
}

pub struct SequentialFileWordSelector {
words: VecDeque<String>,
}

impl SequentialFileWordSelector {
pub fn from_path(path: PathBuf) -> Result<Self, io::Error> {
let file = File::open(&path)?;
let reader = BufReader::new(file);
let words: VecDeque<String> = reader
.lines()
.filter_map(|line| line.ok())
.flat_map(|line| {
let words: Vec<String> = line.split_whitespace().map(String::from).collect();
words.into_iter().filter(|word| {
word.chars()
.all(|c| c.is_ascii() && c.is_alphanumeric() && c.is_ascii_graphic())
})
})
.collect();
Ok(Self { words })
}
}

impl WordSelector for SequentialFileWordSelector {
fn new_word(&mut self) -> Result<String, io::Error> {
match self.words.pop_front() {
Some(word) => Ok(word),
None => Err(io::Error::new(
io::ErrorKind::Other,
"No more words available",
)),
}

/// Wraps another word selector, taking words from it and adding punctuation to the end of or
/// around words with a configurable chance. Will capitalize the next word when an end-of-sentence
/// punctuation mark is used.
Expand Down