1use std::fmt::Display;
2use std::str::FromStr;
3
4use rand::{
5 seq::{IteratorRandom, SliceRandom},
6 thread_rng,
7};
8use thiserror::Error;
9
10use crate::choice::{ChoiceParseError, Choices};
11use crate::interval::Interval;
12use crate::{charset::Charset, choice::Choice};
13
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct PasswordSpec {
16 length: usize,
17 choices: Choices,
18}
19
20impl Default for PasswordSpec {
21 fn default() -> Self {
22 let mut choices = Choices::new();
23 choices.push(Charset::Upper.at_least(1));
24 choices.push(Charset::Lower.at_least(1));
25 choices.push(Charset::Number.at_least(1));
26 choices.push(Charset::Symbol.at_least(1));
27 PasswordSpec {
28 choices,
29 length: 32,
30 }
31 }
32}
33
34#[derive(Debug, Error)]
35pub enum PasswordParseError {
36 #[error("Password spec improperly formatted, expect something like length//interval|charset//interval|charset (likely an internal parsing error)")]
37 ImproperFormat,
38 #[error("Couldn't parse the length segment of the spec `{0}`, expects it to be the first segment of the spec (length//...).")]
39 InvalidLength(String),
40 #[error("Couldn't parse the interval `{0}`.")]
41 BadInterval(String),
42 #[error("Couldn't parse the charset `{0}`.")]
43 BadCharset(String),
44 #[error("{0}")]
45 BadChoice(ChoiceParseError),
46}
47
48impl FromStr for PasswordSpec {
53 type Err = PasswordParseError;
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 let s = s.trim_start();
56 let sep = "//".to_string();
57 let sep_char = sep.chars().last().unwrap();
58 let mut spec = PasswordSpec::new();
60 let mut stack = String::new();
61 let chars: Vec<char> = s.chars().collect();
62 let mut i = 0;
64 while i < chars.len() {
65 let c = chars[i];
66 stack.push(c);
67 i += 1;
68 if stack.ends_with(&sep) {
69 let length: usize = stack[..stack.len() - sep.len()].parse().map_err(|_| {
70 PasswordParseError::InvalidLength(stack[..stack.len() - sep.len()].to_string())
71 })?;
72 spec = spec.length(length);
73 stack = String::new();
74 break;
75 }
76 }
77
78 while i < chars.len() {
80 let c = chars[i];
81 if c != sep_char && stack.ends_with(&sep) {
82 let choice = stack[..stack.len() - sep.len()]
83 .parse()
84 .map_err(PasswordParseError::BadChoice)?;
85 spec = spec.include(choice);
86 stack = String::new();
87 }
88 stack.push(c);
89 i += 1;
90 }
91
92 if stack.ends_with(&sep) {
95 let choice = stack[..stack.len() - sep.len()]
96 .parse()
97 .map_err(PasswordParseError::BadChoice)?;
98 spec = spec.include(choice);
99 stack = String::new();
100 }
101
102 if !stack.is_empty() {
103 let choice = stack[..stack.len()]
104 .parse()
105 .map_err(PasswordParseError::BadChoice)?;
106 spec = spec.include(choice);
107 }
109
110 Ok(spec)
111 }
112}
113
114impl Display for PasswordSpec {
115 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116 write!(f, "{}", self.length)?;
117 write!(f, "{}", self.choices)
118 }
119}
120
121impl PasswordSpec {
122 pub fn new() -> Self {
123 Self {
124 choices: Choices::new(),
125 length: 32,
126 }
127 }
128 pub fn generate(&self) -> Option<String> {
129 if self.check() {
130 let mut characters = vec![];
131 let mut active = Choices::new();
132 for mut choice in self.choices.clone() {
133 characters.extend(choice.get_required());
134 active.push(choice);
135 }
136
137 let remaining = self.length - characters.len();
138 let mut active: Vec<_> = active.into_iter().filter(|x| x.active()).collect();
139
140 for _ in 0..remaining {
141 if let Some(index) = (0..active.len()).choose(&mut thread_rng()) {
142 let c = active[index].next().unwrap();
143 characters.push(c);
144 if !active[index].active() {
145 active.remove(index);
146 }
147 }
148 }
149
150 characters.shuffle(&mut thread_rng());
151 Some(characters.into_iter().collect())
152 } else {
153 None
154 }
155 }
156
157 fn check(&self) -> bool {
158 let mut min_length: usize = 0;
159 let mut max_length: usize = 0;
160 for choice in &self.choices.choices {
161 min_length = min_length.saturating_add(choice.min);
162 max_length = max_length.saturating_add(choice.max);
163 }
164 min_length <= self.length && self.length <= max_length
165 }
166
167 pub fn length(mut self, length: usize) -> Self {
168 self.length = length;
169 self
170 }
171
172 pub fn include(mut self, choice: Choice) -> Self {
173 self.choices.push(choice);
174 self
175 }
176
177 pub fn upper(mut self, interval: Interval) -> Self {
178 self.choices
179 .push(Choice::from_interval(interval, Charset::Upper));
180 self
181 }
182 pub fn upper_at_least(mut self, size: usize) -> Self {
183 self.choices.push(Charset::Upper.at_least(size));
184 self
185 }
186 pub fn upper_at_most(mut self, size: usize) -> Self {
187 self.choices.push(Charset::Upper.at_most(size));
188 self
189 }
190 pub fn upper_exactly(mut self, size: usize) -> Self {
191 self.choices.push(Charset::Upper.exactly(size));
192 self
193 }
194 pub fn lower(mut self, interval: Interval) -> Self {
195 self.choices
196 .push(Choice::from_interval(interval, Charset::Lower));
197
198 self
199 }
200 pub fn lower_at_least(mut self, size: usize) -> Self {
201 self.choices.push(Charset::Lower.at_least(size));
202 self
203 }
204 pub fn lower_at_most(mut self, size: usize) -> Self {
205 self.choices.push(Charset::Lower.at_most(size));
206 self
207 }
208 pub fn lower_exactly(mut self, size: usize) -> Self {
209 self.choices.push(Charset::Lower.exactly(size));
210 self
211 }
212 pub fn number(mut self, interval: Interval) -> Self {
213 self.choices
214 .push(Choice::from_interval(interval, Charset::Number));
215
216 self
217 }
218 pub fn number_at_least(mut self, size: usize) -> Self {
219 self.choices.push(Charset::Number.at_least(size));
220 self
221 }
222 pub fn number_at_most(mut self, size: usize) -> Self {
223 self.choices.push(Charset::Number.at_most(size));
224 self
225 }
226 pub fn number_exactly(mut self, size: usize) -> Self {
227 self.choices.push(Charset::Number.exactly(size));
228 self
229 }
230 pub fn symbol(mut self, interval: Interval) -> Self {
231 self.choices
232 .push(Choice::from_interval(interval, Charset::Symbol));
233
234 self
235 }
236 pub fn symbol_at_least(mut self, size: usize) -> Self {
237 self.choices.push(Charset::Symbol.at_least(size));
238 self
239 }
240 pub fn symbol_at_most(mut self, size: usize) -> Self {
241 self.choices.push(Charset::Symbol.at_most(size));
242 self
243 }
244 pub fn symbol_exactly(mut self, size: usize) -> Self {
245 self.choices.push(Charset::Symbol.exactly(size));
246 self
247 }
248
249 pub fn custom(mut self, chars: Vec<char>, interval: Interval) -> Self {
250 self.choices
251 .push(Choice::from_interval(interval, Charset::Custom(chars)));
252
253 self
254 }
255 pub fn custom_at_least(mut self, chars: Vec<char>, size: usize) -> Self {
256 self.choices.push(Charset::Custom(chars).at_least(size));
257 self
258 }
259 pub fn custom_at_most(mut self, chars: Vec<char>, size: usize) -> Self {
260 self.choices.push(Charset::Custom(chars).at_most(size));
261 self
262 }
263 pub fn custom_exactly(mut self, chars: Vec<char>, size: usize) -> Self {
264 self.choices.push(Charset::Custom(chars).exactly(size));
265 self
266 }
267}