apple_password_gen/
lib.rs1use rand::{Rng, TryRngCore};
2use rand_core::OsRng;
3
4const VOWELS: &'static [u8] = b"aeiouy";
5const CONSONANTS: &'static [u8] = b"bcdfghjkmnpqrstvwxz";
7
8#[must_use]
18pub fn generate() -> String {
19 generate_with_rng(OsRng.unwrap_err())
20}
21
22#[must_use]
29pub fn generate_with_rng<T: rand::CryptoRng + Rng>(mut rng: T) -> String {
30 assert_eq!(VOWELS.len(), 6);
31 assert_eq!(CONSONANTS.len(), 19);
32
33 let pattern_cv = b"cvccvc-cvccvc-cvccvc";
43 let pattern_nu = b".....1.1....1.1....1";
44
45 let number_pos_ct = pattern_nu
46 .iter()
47 .fold(0usize, |acc, &c| if c == b'1' { acc + 1 } else { acc });
48
49 let letter_pos_ct =
50 pattern_cv.iter().fold(
51 0usize,
52 |acc, &c| if c == b'c' || c == b'v' { acc + 1 } else { acc },
53 ) - 1;
54
55 let mut number_i = rng.random_range(0..number_pos_ct);
57 let mut number_pos = None;
58 for (i, p) in pattern_nu.iter().enumerate() {
59 if *p == b'1' {
60 if number_i == 0 {
61 number_pos = Some(i);
62 break;
63 }
64 number_i -= 1;
65 }
66 }
67 let number_pos = number_pos.unwrap();
68
69 let uppercase_pos = rng.random_range(0..letter_pos_ct);
71
72 let mut output = String::with_capacity(pattern_cv.len());
73
74 let mut letter_pos = 0;
76 for (i, p) in pattern_cv.iter().enumerate() {
77 if i == number_pos {
78 output.push_str(&format!("{}", rng.random_range(0..10)));
79 } else {
80 match *p {
81 b'c' => {
82 if letter_pos == uppercase_pos {
83 output.push(
84 CONSONANTS[rng.random_range(0..CONSONANTS.len())].to_ascii_uppercase()
85 as char,
86 );
87 } else {
88 output.push(CONSONANTS[rng.random_range(0..CONSONANTS.len())] as char);
89 }
90 letter_pos += 1;
91 }
92 b'v' => {
93 if letter_pos == uppercase_pos {
94 output.push(
95 VOWELS[rng.random_range(0..VOWELS.len())].to_ascii_uppercase() as char,
96 );
97 } else {
98 output.push(VOWELS[rng.random_range(0..VOWELS.len())] as char);
99 }
100 letter_pos += 1;
101 }
102 _ => {
103 output.push(*p as char);
104 }
105 }
106 }
107 }
108
109 output
110}