use rand::Rng;
use std::fs::{read_dir, DirEntry, File};
use std::io::{BufRead, BufReader};
use crate::actions::Action;
use crate::dirs::setup_dirs;
#[derive(Debug, Clone, PartialEq)]
pub struct PassageInfo {
pub passage: String,
pub title: String,
pub passage_path: String,
}
#[derive(Debug, Clone)]
pub struct Controller {
passages: Vec<PassageInfo>,
current_passage_idx: usize,
history_size: usize,
start_idx: usize,
first_run: bool,
}
impl Controller {
pub fn new(history_size: usize) -> Self {
Controller {
passages: vec![],
current_passage_idx: 0,
history_size,
start_idx: 0,
first_run: true,
}
}
pub fn retrieve_passage(&mut self, action: Action) -> &PassageInfo {
match action {
Action::NextPassage => self.retrieve_next_passage(),
Action::PreviousPassage => self.retrieve_previous_passage(),
Action::RestartPassage => &self.passages[self.current_passage_idx],
Action::Quit => &self.passages[self.current_passage_idx],
}
}
fn retrieve_next_passage(&mut self) -> &PassageInfo {
if self.first_run {
self.first_run = false;
self.passages.push(self.get_new_passage());
} else {
self.current_passage_idx = (self.current_passage_idx + 1) % self.history_size;
if self.current_passage_idx == self.start_idx {
self.start_idx = (self.start_idx + 1) % self.history_size;
if self.passages.len() < self.history_size {
self.passages.push(self.get_new_passage());
} else {
self.passages[self.current_passage_idx] = self.get_new_passage();
}
} else if self.passages.len() < self.history_size
&& self.current_passage_idx == self.passages.len()
{
self.passages.push(self.get_new_passage());
}
}
&self.passages[self.current_passage_idx]
}
fn retrieve_previous_passage(&mut self) -> &PassageInfo {
if self.current_passage_idx != self.start_idx {
if self.current_passage_idx != 0 {
self.current_passage_idx -= 1;
self.current_passage_idx %= self.history_size;
} else {
self.current_passage_idx = self.history_size - 1;
}
}
&self.passages[self.current_passage_idx]
}
pub fn write_initial_passage(&mut self, passage: &str) {
self.passages.push(PassageInfo {
passage: passage.to_owned(),
title: "User input".to_owned(),
passage_path: "User input".to_owned(),
});
}
fn pick_quote_dir(&self) -> DirEntry {
let mut quote_dirs = self.get_quote_dirs();
quote_dirs.remove(rand::thread_rng().gen_range(0, quote_dirs.len()))
}
fn get_quote_dirs(&self) -> Vec<DirEntry> {
self.without_bad_paths(
read_dir(setup_dirs::get_quote_dir().to_string())
.unwrap()
.map(|dir| dir.unwrap())
.collect(),
)
}
fn without_bad_paths(&self, entries: Vec<DirEntry>) -> Vec<DirEntry> {
let mut true_quote_dirs: Vec<DirEntry> = vec![];
for entry in entries {
let str_entry = entry
.path()
.file_stem()
.unwrap()
.to_str()
.unwrap()
.to_string();
if str_entry != "version" && str_entry != ".git" {
true_quote_dirs.push(entry);
}
}
true_quote_dirs
}
#[cfg(not(test))]
fn get_new_passage(&self) -> PassageInfo {
let quote_dir = self.pick_quote_dir();
let num_files: usize = read_dir(quote_dir.path()).unwrap().count();
let random_file_num = rand::thread_rng().gen_range(0, num_files);
let fallback = PassageInfo {
passage: "The quick brown fox jumps over the lazy dog".to_owned(),
title: "darrienglasser.com".to_owned(),
passage_path: "FALLBACK_PATH".to_owned(),
};
if num_files > 0 {
let read_dir_iter = quote_dir.path();
for (count, path) in read_dir(read_dir_iter)
.expect("Failed to read from data dir")
.enumerate()
{
let path = path
.expect("Failed to evaluate path while reading files")
.path();
if count == random_file_num {
let file = File::open(&path).expect("Unable to open quote file");
let mut passage: Vec<String> = vec![];
for line in BufReader::new(file).lines() {
passage.push(line.unwrap());
}
if passage.len() >= 2 {
return PassageInfo {
passage: passage[0].trim().to_string(),
title: passage[1].clone(),
passage_path: path.to_string_lossy().into_owned(),
};
}
}
}
}
fallback
}
#[cfg(test)]
fn get_new_passage(&self) -> PassageInfo {
PassageInfo {
passage: format!("{}", rand::thread_rng().gen_range(0, 10_000_000)),
title: format!("{}", rand::thread_rng().gen_range(0, 10_000_000)),
passage_path: format!("{}", rand::thread_rng().gen_range(0, 10_000_000)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_next_passage_overwrite() {
let mut passage_controller = Controller::new(5);
for _ in 0..4000 {
passage_controller.retrieve_next_passage();
}
assert!(passage_controller.passages.len() == 5);
}
#[test]
fn test_get_vastly_previous_passage() {
let mut passage_controller = Controller::new(5);
passage_controller.retrieve_next_passage();
let mut previous_passage = (*passage_controller.retrieve_previous_passage()).clone();
for _ in 0..4000 {
let passage = (*passage_controller.retrieve_previous_passage()).clone();
assert!(passage == previous_passage);
previous_passage = passage;
}
}
#[test]
fn test_verify_history_integrity() {
let mut passage_controller = Controller::new(5);
passage_controller.retrieve_next_passage();
let passage0 = (*passage_controller.retrieve_passage(Action::PreviousPassage)).clone();
let passage1 = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
let passage2 = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
assert!(passage1 == *passage_controller.retrieve_passage(Action::PreviousPassage));
assert!(passage0 == *passage_controller.retrieve_passage(Action::PreviousPassage));
assert!(passage1 == *passage_controller.retrieve_passage(Action::NextPassage));
assert!(passage2 == *passage_controller.retrieve_passage(Action::NextPassage));
let passage3 = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
let passage4 = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
assert!(passage0 != passage1);
assert!(passage1 != passage2);
assert!(passage2 != passage3);
assert!(passage3 != passage4);
assert!(passage0 != *passage_controller.retrieve_passage(Action::NextPassage));
assert!(passage1 != *passage_controller.retrieve_passage(Action::NextPassage));
assert!(passage2 != *passage_controller.retrieve_passage(Action::NextPassage));
assert!(passage3 != *passage_controller.retrieve_passage(Action::NextPassage));
assert!(passage4 != *passage_controller.retrieve_passage(Action::NextPassage));
}
#[test]
fn test_verify_restart() {
let mut passage_controller = Controller::new(5);
passage_controller.retrieve_next_passage();
let passage0 = (*passage_controller.retrieve_passage(Action::PreviousPassage)).clone();
let passage0_restart =
(*passage_controller.retrieve_passage(Action::RestartPassage)).clone();
assert_eq!(passage0, passage0_restart);
let passage0 = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
let passage0_restart =
(*passage_controller.retrieve_passage(Action::RestartPassage)).clone();
let _ = (*passage_controller.retrieve_passage(Action::NextPassage)).clone();
let passage2 = (*passage_controller.retrieve_passage(Action::PreviousPassage)).clone();
let passage2_restart =
(*passage_controller.retrieve_passage(Action::RestartPassage)).clone();
assert_eq!(passage0, passage0_restart);
assert_eq!(passage2, passage2_restart);
}
}