1use {
6 anyhow::{Result, anyhow},
7 rand::prelude::*,
8 serde::{Deserialize, Serialize},
9 std::{
10 collections::{BTreeMap, BTreeSet, HashMap},
11 fmt::Write,
12 fs::File,
13 io::BufReader,
14 path::Path,
15 },
16 ucfirst::ucfirst,
17};
18
19pub const CONFIG: &str = include_str!("../config.json");
22
23#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
26pub enum WordKind {
27 Adjective,
28 AdjectiveSyllables(usize),
29 Adverb,
30 AdverbSyllables(usize),
31 All,
32 AllSyllables(usize),
33 AllExtended,
34 AllExtendedSyllables(usize),
35 Astronomy,
36 AstronomySyllables(usize),
37 AuxiliaryVerb,
38 AuxiliaryVerbSyllables(usize),
39 Chthonic,
40 ChthonicSyllables(usize),
41 City,
42 CitySyllables(usize),
43 Color,
44 ColorSyllables(usize),
45 Conjunction,
46 ConjunctionSyllables(usize),
47 Continent,
48 ContinentSyllables(usize),
49 Country,
50 CountrySyllables(usize),
51 Day,
52 DaySyllables(usize),
53 Element,
54 ElementSyllables(usize),
55 Extended,
56 ExtendedSyllables(usize),
57 FemaleName,
58 FemaleNameSyllables(usize),
59 GreekMyth,
60 GreekMythSyllables(usize),
61 Interjection,
62 InterjectionSyllables(usize),
63 IntransitiveVerb,
64 IntransitiveVerbSyllables(usize),
65 JupiterMoon,
66 JupiterMoonSyllables(usize),
67 MaleName,
68 MaleNameSyllables(usize),
69 MarsMoon,
70 MarsMoonSyllables(usize),
71 Month,
72 MonthSyllables(usize),
73 Moon,
74 MoonSyllables(usize),
75 Mythology,
76 MythologySyllables(usize),
77 Name,
78 NameSyllables(usize),
79 Nationality,
80 NationalitySyllables(usize),
81 NeptuneMoon,
82 NeptuneMoonSyllables(usize),
83 Noun,
84 NounSyllables(usize),
85 Olympian,
86 OlympianSyllables(usize),
87 Place,
88 PlaceSyllables(usize),
89 Planet,
90 PlanetSyllables(usize),
91 PluralNoun,
92 PluralNounSyllables(usize),
93 Preposition,
94 PrepositionSyllables(usize),
95 Pronoun,
96 PronounSyllables(usize),
97 ProperNoun,
98 ProperNounSyllables(usize),
99 RomanMyth,
100 RomanMythSyllables(usize),
101 SaturnMoon,
102 SaturnMoonSyllables(usize),
103 SingularNoun,
104 SingularNounSyllables(usize),
105 TransitiveVerb,
106 TransitiveVerbSyllables(usize),
107 UranusMoon,
108 UranusMoonSyllables(usize),
109 UsState,
110 UsStateSyllables(usize),
111 Verb,
112 VerbSyllables(usize),
113 VerbPast,
114 VerbPastSyllables(usize),
115}
116
117use WordKind::{
118 Adjective, AdjectiveSyllables, Adverb, AdverbSyllables, All, AllExtended, AllExtendedSyllables,
119 AllSyllables, Astronomy, AstronomySyllables, AuxiliaryVerb, AuxiliaryVerbSyllables, Chthonic,
120 ChthonicSyllables, City, CitySyllables, Color, ColorSyllables, Conjunction,
121 ConjunctionSyllables, Continent, ContinentSyllables, Country, CountrySyllables, Day,
122 DaySyllables, Element, ElementSyllables, Extended, ExtendedSyllables, FemaleName,
123 FemaleNameSyllables, GreekMyth, GreekMythSyllables, Interjection, InterjectionSyllables,
124 IntransitiveVerb, IntransitiveVerbSyllables, JupiterMoon, JupiterMoonSyllables, MaleName,
125 MaleNameSyllables, MarsMoon, MarsMoonSyllables, Month, MonthSyllables, Moon, MoonSyllables,
126 Mythology, MythologySyllables, Name, NameSyllables, Nationality, NationalitySyllables,
127 NeptuneMoon, NeptuneMoonSyllables, Noun, NounSyllables, Olympian, OlympianSyllables, Place,
128 PlaceSyllables, Planet, PlanetSyllables, PluralNoun, PluralNounSyllables, Preposition,
129 PrepositionSyllables, Pronoun, PronounSyllables, ProperNoun, ProperNounSyllables, RomanMyth,
130 RomanMythSyllables, SaturnMoon, SaturnMoonSyllables, SingularNoun, SingularNounSyllables,
131 TransitiveVerb, TransitiveVerbSyllables, UranusMoon, UranusMoonSyllables, UsState,
132 UsStateSyllables, Verb, VerbPast, VerbPastSyllables, VerbSyllables,
133};
134
135const WORD_KINDS_EXTENDED: [WordKind; 9] = [
136 AllExtended,
137 Chthonic,
138 Extended,
139 JupiterMoon,
140 MarsMoon,
141 NeptuneMoon,
142 Olympian,
143 SaturnMoon,
144 UranusMoon,
145];
146
147impl WordKind {
148 fn enumerate(&self, extended: bool) -> Vec<WordKind> {
149 let mut r = vec![*self];
150 match self {
151 Astronomy | Day => r.append(&mut vec![SingularNoun, Noun]),
152 AuxiliaryVerb | IntransitiveVerb | TransitiveVerb | VerbPast => r.push(Verb),
153 Chthonic | Olympian => {
154 r.append(&mut vec![GreekMyth, Mythology, ProperNoun, Noun, Extended]);
155 }
156 City | Country | UsState => r.append(&mut vec![Place, ProperNoun]),
157 Color => r.append(&mut vec![SingularNoun, Noun, Verb]),
158 Continent => r.append(&mut vec![Place, ProperNoun, SingularNoun, Noun]),
159 Element | Mythology => r.append(&mut vec![ProperNoun, Noun]),
160 FemaleName | MaleName => r.append(&mut vec![Name, ProperNoun]),
161 GreekMyth | RomanMyth => r.append(&mut vec![Mythology, ProperNoun, Noun]),
162 JupiterMoon | MarsMoon | NeptuneMoon | SaturnMoon | UranusMoon => {
163 r.append(&mut vec![Moon, Astronomy, SingularNoun, Noun, Extended]);
164 }
165 Month => r.append(&mut vec![SingularNoun, Noun, ProperNoun]),
166 Moon | Planet => r.append(&mut vec![Astronomy, SingularNoun, Noun]),
167 Name => r.push(ProperNoun),
168 Nationality => r.push(Adjective),
169 PluralNoun | SingularNoun => r.push(Noun),
170 _ => {}
171 }
172 if extended || !r.contains(&Extended) {
173 r.push(All);
174 }
175 r.push(AllExtended);
176 r
177 }
178
179 fn sub(&self) -> String {
180 match self {
181 Adjective => String::from("{adj}"),
182 Adverb => String::from("{adv}"),
183 All => String::from("{a}"),
184 AllExtended => String::from("{a.ext}"),
185 Astronomy => String::from("{ast}"),
186 AuxiliaryVerb => String::from("{v.aux}"),
187 Chthonic => String::from("{chthonic}"),
188 City => String::from("{city}"),
189 Color => String::from("{color}"),
190 Conjunction => String::from("{conj}"),
191 Continent => String::from("{cont}"),
192 Country => String::from("{country}"),
193 Day => String::from("{day}"),
194 Element => String::from("{el}"),
195 Extended => String::from("{ext}"),
196 FemaleName => String::from("{fname}"),
197 GreekMyth => String::from("{greekmyth}"),
198 Interjection => String::from("{i}"),
199 IntransitiveVerb => String::from("{v.int}"),
200 JupiterMoon => String::from("{jupitermoon}"),
201 MaleName => String::from("{mname}"),
202 MarsMoon => String::from("{marsmoon}"),
203 Month => String::from("{mon}"),
204 Moon => String::from("{moon}"),
205 Mythology => String::from("{myth}"),
206 Name => String::from("{name}"),
207 Nationality => String::from("{nat}"),
208 NeptuneMoon => String::from("{neptunemoon}"),
209 Noun => String::from("{n}"),
210 Olympian => String::from("{olympian}"),
211 Place => String::from("{place}"),
212 Planet => String::from("{planet}"),
213 PluralNoun => String::from("{n.pl}"),
214 Preposition => String::from("{prep}"),
215 Pronoun => String::from("{n.pro}"),
216 ProperNoun => String::from("{n.prop}"),
217 RomanMyth => String::from("{romanmyth}"),
218 SaturnMoon => String::from("{saturnmoon}"),
219 SingularNoun => String::from("{n.s}"),
220 TransitiveVerb => String::from("{v.tr}"),
221 UranusMoon => String::from("{uranusmoon}"),
222 UsState => String::from("{us-state}"),
223 Verb => String::from("{v}"),
224 VerbPast => String::from("{v.past}"),
225 AdjectiveSyllables(n) => format!("{{adj:{n}}}"),
226 AdverbSyllables(n) => format!("{{adv:{n}}}"),
227 AllSyllables(n) => format!("{{a:{n}}}"),
228 AllExtendedSyllables(n) => format!("{{a.ext:{n}}}"),
229 AstronomySyllables(n) => format!("{{ast:{n}}}"),
230 AuxiliaryVerbSyllables(n) => format!("{{v.aux:{n}}}"),
231 ChthonicSyllables(n) => format!("{{chthonic:{n}}}"),
232 CitySyllables(n) => format!("{{city:{n}}}"),
233 ColorSyllables(n) => format!("{{color:{n}}}"),
234 ConjunctionSyllables(n) => format!("{{conj:{n}}}"),
235 ContinentSyllables(n) => format!("{{cont:{n}}}"),
236 CountrySyllables(n) => format!("{{country:{n}}}"),
237 DaySyllables(n) => format!("{{day:{n}}}"),
238 ElementSyllables(n) => format!("{{el:{n}}}"),
239 ExtendedSyllables(n) => format!("{{ext:{n}}}"),
240 FemaleNameSyllables(n) => format!("{{fname:{n}}}"),
241 GreekMythSyllables(n) => format!("{{greekmyth:{n}}}"),
242 InterjectionSyllables(n) => format!("{{i:{n}}}"),
243 IntransitiveVerbSyllables(n) => format!("{{v.int:{n}}}"),
244 JupiterMoonSyllables(n) => format!("{{jupitermoon:{n}}}"),
245 MaleNameSyllables(n) => format!("{{mname:{n}}}"),
246 MarsMoonSyllables(n) => format!("{{marsmoon:{n}}}"),
247 MonthSyllables(n) => format!("{{mon:{n}}}"),
248 MoonSyllables(n) => format!("{{moon:{n}}}"),
249 MythologySyllables(n) => format!("{{myth:{n}}}"),
250 NameSyllables(n) => format!("{{name:{n}}}"),
251 NationalitySyllables(n) => format!("{{nat:{n}}}"),
252 NeptuneMoonSyllables(n) => format!("{{neptunemoon:{n}}}"),
253 NounSyllables(n) => format!("{{n:{n}}}"),
254 OlympianSyllables(n) => format!("{{olympian:{n}}}"),
255 PlaceSyllables(n) => format!("{{place:{n}}}"),
256 PlanetSyllables(n) => format!("{{planet:{n}}}"),
257 PluralNounSyllables(n) => format!("{{n.pl:{n}}}"),
258 PrepositionSyllables(n) => format!("{{prep:{n}}}"),
259 PronounSyllables(n) => format!("{{n.pro:{n}}}"),
260 ProperNounSyllables(n) => format!("{{n.prop:{n}}}"),
261 RomanMythSyllables(n) => format!("{{romanmyth:{n}}}"),
262 SaturnMoonSyllables(n) => format!("{{saturnmoon:{n}}}"),
263 SingularNounSyllables(n) => format!("{{n.s:{n}}}"),
264 TransitiveVerbSyllables(n) => format!("{{v.tr:{n}}}"),
265 UranusMoonSyllables(n) => format!("{{uranusmoon:{n}}}"),
266 UsStateSyllables(n) => format!("{{us-state:{n}}}"),
267 VerbSyllables(n) => format!("{{v:{n}}}"),
268 VerbPastSyllables(n) => format!("{{v.past:{n}}}"),
269 }
270 }
271
272 fn with_syllables(&self, syllables: usize) -> WordKind {
273 match self {
274 Adjective => AdjectiveSyllables(syllables),
275 Adverb => AdverbSyllables(syllables),
276 All => AllSyllables(syllables),
277 AllExtended => AllExtendedSyllables(syllables),
278 Astronomy => AstronomySyllables(syllables),
279 AuxiliaryVerb => AuxiliaryVerbSyllables(syllables),
280 Chthonic => ChthonicSyllables(syllables),
281 City => CitySyllables(syllables),
282 Color => ColorSyllables(syllables),
283 Conjunction => ConjunctionSyllables(syllables),
284 Continent => ContinentSyllables(syllables),
285 Country => CountrySyllables(syllables),
286 Day => DaySyllables(syllables),
287 Element => ElementSyllables(syllables),
288 Extended => ExtendedSyllables(syllables),
289 FemaleName => FemaleNameSyllables(syllables),
290 GreekMyth => GreekMythSyllables(syllables),
291 Interjection => InterjectionSyllables(syllables),
292 IntransitiveVerb => IntransitiveVerbSyllables(syllables),
293 JupiterMoon => JupiterMoonSyllables(syllables),
294 MaleName => MaleNameSyllables(syllables),
295 MarsMoon => MarsMoonSyllables(syllables),
296 Month => MonthSyllables(syllables),
297 Moon => MoonSyllables(syllables),
298 Mythology => MythologySyllables(syllables),
299 Name => NameSyllables(syllables),
300 Nationality => NationalitySyllables(syllables),
301 NeptuneMoon => NeptuneMoonSyllables(syllables),
302 Noun => NounSyllables(syllables),
303 Olympian => OlympianSyllables(syllables),
304 Place => PlaceSyllables(syllables),
305 Planet => PlanetSyllables(syllables),
306 PluralNoun => PluralNounSyllables(syllables),
307 Preposition => PrepositionSyllables(syllables),
308 Pronoun => PronounSyllables(syllables),
309 ProperNoun => ProperNounSyllables(syllables),
310 RomanMyth => RomanMythSyllables(syllables),
311 SaturnMoon => SaturnMoonSyllables(syllables),
312 SingularNoun => SingularNounSyllables(syllables),
313 TransitiveVerb => TransitiveVerbSyllables(syllables),
314 UranusMoon => UranusMoonSyllables(syllables),
315 UsState => UsStateSyllables(syllables),
316 Verb => VerbSyllables(syllables),
317 VerbPast => VerbPastSyllables(syllables),
318 _ => *self,
319 }
320 }
321}
322
323#[derive(Clone, Deserialize, Serialize)]
326pub struct WordDetails {
327 kinds: Vec<WordKind>,
328 syllables: usize,
329}
330
331#[derive(Clone, Deserialize, Serialize)]
334pub struct Config {
335 characters: BTreeMap<char, String>,
336 words: BTreeMap<String, WordDetails>,
337
338 #[serde(skip)]
339 words_built: BTreeMap<String, WordDetails>,
340
341 #[serde(skip)]
342 kinds: BTreeMap<WordKind, Vec<String>>,
343
344 #[serde(skip)]
345 alphabets: BTreeMap<char, Vec<char>>,
346
347 #[serde(skip)]
348 pub subs: BTreeMap<String, WordKind>,
349
350 #[serde(skip)]
351 pub extended: bool,
352}
353
354impl Config {
355 pub fn from_path(path: &Path, extended: bool) -> Result<Config> {
361 let r: Config = serde_json::from_reader(BufReader::new(File::open(path)?))?;
362 Ok(r.build(extended))
363 }
364
365 pub fn from_str(s: &str, extended: bool) -> Result<Config> {
371 Ok(serde_json::from_str::<Config>(s)?.build(extended))
372 }
373
374 pub fn dump(&self) -> Result<String> {
380 Ok(serde_json::to_string_pretty(self)?)
381 }
382
383 fn build(mut self, extended: bool) -> Config {
384 let mut kinds: HashMap<WordKind, BTreeSet<String>> = HashMap::new();
386 self.words_built = BTreeMap::new();
387 for (word, word_details) in &self.words {
388 let word_kinds = word_details
389 .kinds
390 .iter()
391 .flat_map(|x| x.enumerate(extended))
392 .collect::<Vec<_>>();
393 let word_is_extended = word_kinds.contains(&Extended);
394 for kind in &word_kinds {
395 let kind_is_extended = WORD_KINDS_EXTENDED.contains(kind);
396 if extended || !word_is_extended || kind_is_extended {
397 let s = kinds.entry(*kind).or_default();
398 s.insert(word.clone());
399 let s = kinds
400 .entry(kind.with_syllables(word_details.syllables))
401 .or_default();
402 s.insert(word.clone());
403 }
404 }
405 self.words_built.insert(
406 word.clone(),
407 WordDetails {
408 kinds: word_kinds,
409 syllables: word_details.syllables,
410 },
411 );
412 }
413 self.kinds = kinds
414 .into_iter()
415 .map(|(k, v)| (k, v.into_iter().collect()))
416 .collect();
417 self.subs = self
418 .kinds
419 .keys()
420 .flat_map(|x| {
421 let sub = x.sub();
422 [
423 (sub.clone(), *x),
424 (format!("{{W:{}", &sub[1..]), *x),
425 (format!("{{T:{}", &sub[1..]), *x),
426 ]
427 })
428 .collect();
429
430 for (c, s) in &self.characters {
432 self.alphabets.insert(*c, s.chars().collect());
433 }
434
435 self.extended = extended;
436
437 self
438 }
439
440 pub fn list(&self, sub: &str) -> Result<Vec<String>> {
446 if let Some(kind) = self.subs.get(sub) {
447 if let Some(list) = self.kinds.get(kind) {
448 Ok(if sub.starts_with("{W:") {
449 list.iter().map(|x| x.to_uppercase()).collect()
450 } else if sub.starts_with("{T:") {
451 list.iter().map(|x| ucfirst(x)).collect()
452 } else {
453 list.clone()
454 })
455 } else {
456 Err(anyhow!("Unknown"))
457 }
458 } else {
459 Err(anyhow!("Invalid sub: `{sub}`!"))
460 }
461 }
462
463 fn get_n(&self, n: usize, kind: WordKind) -> Vec<String> {
464 self.kinds
465 .get(&kind)
466 .unwrap()
467 .sample(&mut rand::rng(), n)
468 .cloned()
469 .collect()
470 }
471
472 fn get(&self, kind: WordKind) -> String {
473 self.kinds
474 .get(&kind)
475 .unwrap()
476 .choose(&mut rand::rng())
477 .unwrap()
478 .clone()
479 }
480
481 fn keychain_word_1(&self) -> String {
483 shuffle(&format!(
484 "{}{}",
485 random_str(5, self.alphabets.get(&'c').unwrap()),
486 random_str(1, self.alphabets.get(&'C').unwrap()),
487 ))
488 }
489
490 fn keychain_word_2(&self) -> String {
492 shuffle(&format!(
493 "{}{}",
494 random_str(5, self.alphabets.get(&'c').unwrap()),
495 random_str(1, self.alphabets.get(&'d').unwrap()),
496 ))
497 }
498
499 fn keychain_word_3(&self) -> String {
501 random_str(6, self.alphabets.get(&'c').unwrap())
502 }
503
504 fn keychain_words(&self, n: usize) -> Vec<String> {
512 let mut words = vec![];
513 if n == 0 {
514 return words;
515 }
516 words.push(self.keychain_word_1());
517 if n >= 2 {
518 words.push(self.keychain_word_2());
519 }
520 if n >= 3 {
521 words.push(self.keychain_word_3());
522 }
523 let mut rng = rand::rng();
524 for _ in 4..=n {
525 words.push(match KEYCHAIN_WORDS.choose(&mut rng).unwrap() {
526 KeychainWord1 => self.keychain_word_1(),
527 KeychainWord2 => self.keychain_word_2(),
528 KeychainWord3 => self.keychain_word_3(),
529 });
530 }
531 words.shuffle(&mut rng);
532 words
533 }
534
535 #[must_use]
537 pub fn keychain(&self) -> String {
538 self.keychain_words(3).join("-")
539 }
540
541 #[must_use]
543 pub fn codename(&self) -> String {
544 format!(
545 "{} {}",
546 self.get(Adjective).to_uppercase(),
547 self.get(Noun).to_uppercase(),
548 )
549 }
550
551 #[must_use]
568 pub fn codename_series(&self, n: usize) -> String {
569 let mut adjectives = self
570 .get_n(n + 1, Adjective)
571 .iter()
572 .map(|x| x.to_uppercase())
573 .collect::<Vec<_>>();
574 let mut nouns = self
575 .get_n(n + 1, Noun)
576 .iter()
577 .map(|x| x.to_uppercase())
578 .collect::<Vec<_>>();
579 let umbrella = format!("{}{}", adjectives.remove(0), nouns.remove(0));
580 let mut r = vec![];
581 while !adjectives.is_empty() {
582 r.push(format!(
583 "{umbrella} {}{}",
584 adjectives.remove(0),
585 nouns.remove(0),
586 ));
587 }
588 r.join("\n")
589 }
590
591 #[allow(clippy::missing_panics_doc)]
593 #[must_use]
594 pub fn haiku(&self, variant: HaikuVariant) -> String {
595 let (add_syllables, kebab, newline) = variant.options();
596 let (word_sep, line_sep, syl_sep) = if newline {
597 (" ", "\n", " ")
598 } else if kebab {
599 ("-", "/", "")
600 } else {
601 (" ", " / ", " ")
602 };
603 let max = 5;
604
605 let mut rng = rand::rng();
606
607 let mut lines = vec![];
608 let mut w = BTreeMap::new();
609
610 for mut c in [5, 7, 5] {
612 let mut words = vec![];
613 while c > 0 {
614 let poss = (1..=std::cmp::min(c, max)).collect::<Vec<_>>();
615 let i = poss.choose(&mut rng).unwrap();
616 words.push(*i);
617 c -= *i;
618 w.entry(*i).and_modify(|e| *e += 1).or_insert(1);
619 }
620 lines.push(words);
621 }
622
623 let mut w = w
625 .iter()
626 .map(|(syllables, words)| {
627 (
628 *syllables,
629 self.get_n(
630 *words,
631 if self.extended {
632 AllExtendedSyllables(*syllables)
633 } else {
634 AllSyllables(*syllables)
635 },
636 ),
637 )
638 })
639 .collect::<BTreeMap<usize, Vec<String>>>();
640
641 let mut r = vec![];
642
643 for line in &lines {
644 let mut words = vec![];
645 for (i, syllables) in line.iter().enumerate() {
646 let s = w.get_mut(syllables).unwrap();
647 let word = s.remove(0);
648 let details = self.words_built.get(&word).unwrap();
649 let word = if i == 0 || details.kinds.contains(&ProperNoun) {
650 ucfirst(&word)
651 } else {
652 word
653 };
654 let word = word
655 .replace("Newhampshire", "New Hampshire")
656 .replace("Newjersey", "New Jersey")
657 .replace("Newmexico", "New Mexico")
658 .replace("Newyork", "New York")
659 .replace("Northamerica", "North America")
660 .replace("Northcarolina", "North Carolina")
661 .replace("Northdakota", "North Dakota")
662 .replace("Rhodeisland", "Rhode Island")
663 .replace("Southamerica", "South America")
664 .replace("Southcarolina", "South Carolina")
665 .replace("Southdakota", "South Dakota")
666 .replace("Westvirginia", "West Virginia");
667 words.push(if i == 0 { ucfirst(&word) } else { word });
668 }
669 let mut words = words.join(word_sep);
670 if add_syllables {
671 write!(
672 words,
673 "{syl_sep}({})",
674 line.iter()
675 .map(ToString::to_string)
676 .collect::<Vec<_>>()
677 .join(",")
678 )
679 .unwrap();
680 }
681 r.push(words);
682 }
683
684 r.join(line_sep)
685 }
686
687 #[allow(clippy::missing_panics_doc)]
689 #[must_use]
690 pub fn generate(&self, pattern: &str) -> String {
691 let j = pattern.len();
692 let mut subs = vec![];
693 let mut words = 0;
694 let mut kc_words = 0;
695 let mut pre = BTreeMap::new();
696 'outer: for (sub, kind) in &self.subs {
697 let mut i = 0;
698 while i < j {
699 if let Some(p) = pattern[i..].find(sub) {
700 let p = i + p;
701 pre.insert(p, (sub, *kind));
702 i = p + sub.len();
703 } else {
704 continue 'outer;
705 }
706 }
707 }
708 let mut i = 0;
709 for (p, (sub, kind)) in &pre {
710 let s = &pattern[i..*p];
711 if *p > i {
712 words += s
713 .chars()
714 .map(|c| usize::from(['w', 'W', 'T'].contains(&c)))
715 .sum::<usize>();
716 kc_words += s.chars().map(|c| usize::from(c == 'k')).sum::<usize>();
717 subs.push(Sub::Pattern(s));
718 }
719 subs.push(Sub::Special(((*sub).clone(), *kind)));
720 i = *p + sub.len();
721 }
722 if subs.is_empty() {
723 words += pattern
724 .chars()
725 .map(|c| usize::from(['w', 'W', 'T'].contains(&c)))
726 .sum::<usize>();
727 kc_words += pattern
728 .chars()
729 .map(|c| usize::from(c == 'k'))
730 .sum::<usize>();
731 subs.push(Sub::Pattern(pattern));
732 } else if i < j {
733 subs.push(Sub::Pattern(&pattern[i..j]));
734 }
735
736 let mut words = self.get_n(words, if self.extended { AllExtended } else { All });
737 let mut kc_words = self.keychain_words(kc_words);
738
739 let mut rng = rand::rng();
740 let mut r = String::new();
741
742 for sub in subs {
743 match sub {
744 Sub::Pattern(s) => {
745 for c in s.chars() {
746 match c {
747 'W' => r.push_str(&words.remove(0).to_uppercase()),
748 'w' => r.push_str(&words.remove(0)),
749 'T' => r.push_str(&ucfirst(&words.remove(0))),
750 'k' => r.push_str(&kc_words.remove(0)),
751 _ => {
752 if let Some(alphabet) = self.alphabets.get(&c) {
753 r.push(*alphabet.choose(&mut rng).unwrap());
754 } else {
755 r.push(c);
756 }
757 }
758 }
759 }
760 }
761 Sub::Special((sub, kind)) => {
762 let word = self.get(kind);
763 let word = if sub.starts_with("{W:") {
764 word.to_uppercase()
765 } else if sub.starts_with("{T:") {
766 ucfirst(&word)
767 } else {
768 word
769 };
770 r.push_str(&word);
771 }
772 }
773 }
774 r
775 }
776}
777
778impl Default for Config {
779 fn default() -> Config {
780 Config::from_str(CONFIG, false).unwrap()
781 }
782}
783
784#[derive(Clone, Copy)]
787pub enum HaikuVariant {
788 Normal,
789 WithSyllables,
790 WithSyllablesCondensed,
791 Condensed,
792 Full,
793 FullWithSyllables,
794}
795
796impl HaikuVariant {
797 fn options(self) -> (bool, bool, bool) {
798 match self {
799 Self::Condensed => (false, true, false),
800 Self::WithSyllablesCondensed => (true, true, false),
801 Self::Normal => (false, false, false),
802 Self::WithSyllables => (true, false, false),
803 Self::Full => (false, false, true),
804 Self::FullWithSyllables => (true, false, true),
805 }
806 }
807}
808#[derive(Debug)]
811enum Sub<'a> {
812 Pattern(&'a str),
813 Special((String, WordKind)),
814}
815
816enum KeychainWord {
819 KeychainWord1,
820 KeychainWord2,
821 KeychainWord3,
822}
823
824use KeychainWord::{KeychainWord1, KeychainWord2, KeychainWord3};
825
826const KEYCHAIN_WORDS: [KeychainWord; 3] = [KeychainWord1, KeychainWord2, KeychainWord3];
827
828fn random_str(n: usize, alphabet: &[char]) -> String {
832 let len = alphabet.len();
833 let mut r = String::new();
834 for _ in 0..n {
835 r.push(alphabet[rand::rng().random_range(0..len)]);
836 }
837 r
838}
839
840#[must_use]
842pub fn shuffle(s: &str) -> String {
843 let mut rng = rand::rng();
844 let mut r = s.chars().collect::<Vec<_>>();
845 r.shuffle(&mut rng);
846 r.into_iter().collect()
847}