rusty-diceware

Commandline diceware, with or without dice, written in Rustlang.
git clone https://kaka.farm/~git/rusty-diceware
Log | Files | Refs | README | LICENSE

commit e4b4eaeedf6fe783e583d36685943a00e8e1b340
parent a0e2eb496e425865322875462c4cd8be5b31c0c5
Author: Yuval Langer <yuval.langer@gmail.com>
Date:   Wed,  6 Nov 2024 20:21:53 +0200

Convert both word generating methods - RNG and die casting - into two Iterator types.

Diffstat:
Msrc/main.rs | 164+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 81 insertions(+), 83 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -66,42 +66,30 @@ fn unknown_wordlist(wordlist_name: &str) -> ! { exit(-1) } -fn print_words( - words: &Vec<String>, - delimiter: char, -) { - for word in 0..(words.len() - 1) { - print!("{}{}", words[word], delimiter); - } - print!("{}", words[words.len() - 1]); +struct ReadWordDieCasts<'a, T> { + bytes_iter: &'a mut std::io::Bytes<T>, + wordlist: &'a Vec<&'a str>, +} - println!(); +impl<T> ReadWordDieCasts<'_, T> { + fn new<'a, D>( + bytes_iter: &'a mut std::io::Bytes<T>, + wordlist: &'a Vec<&'a str>, + ) -> ReadWordDieCasts<'a, T> { + ReadWordDieCasts { + bytes_iter, + wordlist, + } + } } -// struct ReadWords<'a, T: std::iter::Iterator> { -// bytes_iter: &'a mut std::io::Bytes<T>, -// wordlist: &'a Vec<&'a str> -// } - -// impl<'a, T: std::iter::Iterator> ReadWords<'a, T> { -// fn new( -// bytes_iter: &'a mut std::io::Bytes<T>, -// wordlist: &'a Vec<&'a str> -// ) -> ReadWords<'a, T> { -// ReadWords { -// bytes_iter, -// wordlist, -// } -// } -// } - -// impl<'a, T: std::iter::Iterator<Item = Result<u8, std::io::Error>> + std::fmt::Display + std::io::Read> Iterator for ReadWords<'a, T> { -// type Item = String; - -// fn next(&mut self) -> Option<<Self as Iterator>::Item> { -// return read_single_word::<T, T>(self.bytes_iter, self.wordlist); -// } -// } +impl<T: std::io::Read> Iterator for ReadWordDieCasts<'_, T> { + type Item = String; + + fn next(&mut self) -> Option<<ReadWordDieCasts<T> as Iterator>::Item> { + return read_single_word(self.bytes_iter, self.wordlist); + } +} fn read_single_word<T>( bytes_iter: &mut std::io::Bytes<T>, @@ -196,28 +184,6 @@ fn read_single_word<T>( return Some(wordlist[word_index].to_string()); } -fn read_words_from_rolls( - wordlist: &Vec<&str>, -) -> Vec<String> { - let stdin = std::io::stdin(); - let mut words = Vec::new(); - let mut bytes_iter = stdin.bytes(); - - loop { - match read_single_word(&mut bytes_iter, wordlist) { - Some(word) => words.push(word), - None => break, - }; - }; - - // TODO: Maybe use an iterator? - // for word in ReadWords::new(&mut bytes_iter, wordlist) { - // words.push(word) - // }; - - words -} - fn entropy(wordlist: &[&str]) -> f64 { (wordlist.len() as f64).log2() } @@ -226,23 +192,29 @@ fn entropyn(wordlist: Vec<&str>, n: u64) -> f64 { entropy(&wordlist) * (n as f64) } -fn read_rng_rolls( - rng: &mut ThreadRng, - wordlist: &Vec<&str>, - word_num: u64, -) -> Vec<String> { - /* - From base 6 conversion: - (0..6).pow(1) = (0, 1, ..6) - (0..6).pow(2) = (0, 6, ..36) - (0..6).pow(3) = (0, 36, ..216) - */ - - return (0..word_num) - .collect::<Vec<_>>() - .iter() - .map(|_| wordlist[rng.gen_range(0..wordlist.len())].to_string()) - .collect(); +struct ReadRngWord<'a> { + rng: &'a mut ThreadRng, + wordlist: &'a Vec<&'a str>, +} + +impl<'a> ReadRngWord<'a> { + fn new( + rng: &'a mut ThreadRng, + wordlist: &'a Vec<&'a str>, + ) -> ReadRngWord<'a> { + ReadRngWord { + rng, + wordlist, + } + } +} + +impl<'a> Iterator for ReadRngWord<'a> { + type Item = String; + + fn next(&mut self) -> Option<String> { + Some(self.wordlist[self.rng.gen_range(0..self.wordlist.len())].to_string()) + } } fn load_wordlist_file(filepath: &str) -> String { @@ -315,23 +287,49 @@ fn main() { wordlist_name.get_list().to_vec() }; - let words = if is_physical_rolls { - read_words_from_rolls(&wordlist) + if is_physical_rolls { + let stdin = std::io::stdin(); + let mut bytes_iter = stdin.bytes(); + + let mut words = ReadWordDieCasts::new::<std::io::Bytes<std::io::Stdin>>(&mut bytes_iter, &wordlist); + + if let Some(word) = words.next() { + print!("{}", word); + + let words_count = 1; + + for word in words { + print!("{}{}", delimiter, word); + } + println!(); + + if is_entropy_printed { + println!("{}", entropyn(wordlist, words_count)); + } + }; } else if word_num != 0 { let mut rng = thread_rng(); - read_rng_rolls(&mut rng, &wordlist, word_num) + + let mut words = ReadRngWord::new(&mut rng, &wordlist); + + print!("{}", words.next().unwrap()); + + let mut words_count = 1; + + for _ in 1..word_num { + print!("{}{}", delimiter, words.next().unwrap()); + words_count += 1; + }; + + println!(); + + if is_entropy_printed { + println!("{}", entropyn(wordlist, words_count)); + } } else { print_usage(program, opts); exit(-1); }; - - if words.len() > 0 { - print_words(&words, delimiter); - } - - if is_entropy_printed { - println!("{}", entropyn(wordlist, words.len() as u64)); - } } #[cfg(test)]