aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-04-17 02:02:18 -0400
committerJesse Luehrs <doy@tozt.net>2021-04-17 02:02:18 -0400
commitc248016ea7ebc20551913cc1dfb5dc022fa3440d (patch)
tree1961a74e3862509220f30fe77366ae34163c6803
parent9e77724efff281f0fe6d05440ad65c5ab561f380 (diff)
downloadrbw-c248016ea7ebc20551913cc1dfb5dc022fa3440d.tar.gz
rbw-c248016ea7ebc20551913cc1dfb5dc022fa3440d.zip
choose characters from alphabet with replacement for pwgen
-rw-r--r--CHANGELOG.md6
-rw-r--r--src/pwgen.rs44
2 files changed, 49 insertions, 1 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3e3754e..61bcf82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## [Unreleased]
+
+### Fixed
+
+* `rbw generate` can now choose the same character more than once (#54, rjc)
+
## [1.1.2] - 2021-03-06
### Fixed
diff --git a/src/pwgen.rs b/src/pwgen.rs
index dc6808f..cf63bb2 100644
--- a/src/pwgen.rs
+++ b/src/pwgen.rs
@@ -56,8 +56,50 @@ pub fn pwgen(ty: Type, len: usize) -> String {
let mut rng = rand::thread_rng();
let mut pass = vec![];
- pass.extend(alphabet.choose_multiple(&mut rng, len).copied());
+ pass.extend(
+ std::iter::repeat_with(|| alphabet.choose(&mut rng).unwrap())
+ .take(len),
+ );
// unwrap is safe because the method of generating passwords guarantees
// valid utf8
String::from_utf8(pass).unwrap()
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_pwgen() {
+ let pw = pwgen(Type::AllChars, 50);
+ assert_eq!(pw.len(), 50);
+ // technically this could fail, but the chances are incredibly low
+ // (around 0.000009%)
+ assert_duplicates(&pw);
+
+ let pw = pwgen(Type::AllChars, 100);
+ assert_eq!(pw.len(), 100);
+ assert_duplicates(&pw);
+
+ let pw = pwgen(Type::NoSymbols, 100);
+ assert_eq!(pw.len(), 100);
+ assert_duplicates(&pw);
+
+ let pw = pwgen(Type::Numbers, 100);
+ assert_eq!(pw.len(), 100);
+ assert_duplicates(&pw);
+
+ let pw = pwgen(Type::NonConfusables, 100);
+ assert_eq!(pw.len(), 100);
+ assert_duplicates(&pw);
+ }
+
+ #[track_caller]
+ fn assert_duplicates(s: &str) {
+ let mut set = std::collections::HashSet::new();
+ for c in s.chars() {
+ set.insert(c);
+ }
+ assert!(set.len() < s.len())
+ }
+}