use r2d2_sqlite::SqliteConnectionManager; use r2d2::Pool; #[macro_use] use rusqlite::params; use std::sync::Arc; use std::fs::File; use std::io::{self, BufRead}; use rand::thread_rng; use rand::seq::SliceRandom; use rand::distributions::{Distribution, WeightedIndex}; pub fn create_array_source(filename: &str) -> Arc> { let file = File::open(filename).unwrap(); let lines = io::BufReader::new(file).lines(); let mut questions: Vec = Vec::new(); for line in lines { if let Ok(l) = line { questions.push(l); } } Arc::new(ArraySource::create(questions)) } pub trait DataSource { fn get_ith(&self, i: usize) -> Option; fn len(&self) -> usize; } pub struct ArraySource { words: Vec } impl DataSource for ArraySource { fn get_ith(&self, i: usize) -> Option { self.words.get(i).cloned() } fn len(&self) -> usize { self.words.len() } } impl ArraySource { pub fn create(words: Vec) -> Self { ArraySource { words } } } pub struct SqliteSource { pool: Pool } impl DataSource for SqliteSource { fn get_ith(& self, i: usize) -> Option { match self.pool.get() { Ok(p) => { let r: rusqlite::Result = p.query_row( "SELECT question FROM questions WHERE id=?1;", params![i as i32], |row| row.get(0)); r.ok() }, Err(_) => None } } fn len(&self) -> usize { match self.pool.get() { Ok(p) => { let r: rusqlite::Result = p.query_row( "SELECT COUNT(*) FROM questions;", rusqlite::NO_PARAMS, |row| row.get(0)); r.unwrap_or(0) as usize }, Err(_) => 0 } } } impl SqliteSource { pub fn open(db_name: &str) -> Result { Ok(SqliteSource { pool: Pool::new(SqliteConnectionManager::file(db_name))? }) } } pub struct Shuffler { source: Arc>, permutation: Vec, index: usize } impl Shuffler { pub fn create(source: Arc>) -> Self { let mut permutation = (0..source.len()).collect::>(); permutation.shuffle(&mut thread_rng()); Self { source, permutation, index: 0 } } pub fn get(&mut self) -> T { let old_index = self.index; self.index += 1; if self.index >= self.permutation.len() { self.index = 0; } self.source.get_ith(self.permutation[old_index]).unwrap() } } pub struct LetterDistribution { consonants: Vec, consonant_weights: WeightedIndex, vowels: Vec, vowel_weights: WeightedIndex, } impl LetterDistribution { pub fn create() -> Self { Self { consonants: vec![ 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' ], consonant_weights: WeightedIndex::new(vec![ 4.5f32, 5.5f32, 6.5f32, 3.5f32, 5.0f32, 7.5f32, 1.0f32, 3.0f32, 5.0f32, 5.0f32, 13.0f32, 2.5f32, 1.0f32, 9.0f32, 8.5f32, 8.5f32, 2.5f32, 3.0f32, 1.0f32, 1.0f32, 3.5f32, ]).unwrap(), vowels: vec!['A', 'E', 'I', 'O', 'U', 'Ä', 'Ö', 'Ü'], vowel_weights: WeightedIndex::new(vec![ 17.0f32, 32.0f32, 22.0f32, 10.0f32, 13.0f32, 2.0f32, 2.0f32, 2.0f32, ]).unwrap() } } pub fn get(&self, vowels: usize, consonants: usize) -> Vec { let mut result: Vec = Vec::new(); let rng = &mut thread_rng(); for _i in 0..vowels { result.push(self.vowels[self.vowel_weights.sample(rng)]); } for _i in 0..consonants { result.push(self.consonants[self.consonant_weights.sample(rng)]); } return result; } }