1use rand::seq::SliceRandom;
7use rand::Rng;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
13#[serde(rename_all = "snake_case")]
14pub enum NameCulture {
15 #[default]
17 WesternUs,
18 German,
20 French,
22 Chinese,
24 Japanese,
26 Indian,
28 Hispanic,
30}
31
32impl NameCulture {
33 pub fn all() -> &'static [NameCulture] {
35 &[
36 NameCulture::WesternUs,
37 NameCulture::German,
38 NameCulture::French,
39 NameCulture::Chinese,
40 NameCulture::Japanese,
41 NameCulture::Indian,
42 NameCulture::Hispanic,
43 ]
44 }
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct PersonName {
50 pub first_name: String,
52 pub last_name: String,
54 pub display_name: String,
56 pub culture: NameCulture,
58 pub is_male: bool,
60}
61
62impl PersonName {
63 pub fn to_user_id(&self, index: usize) -> String {
65 let first_initial = self.first_name.chars().next().unwrap_or('X');
66 let last_part: String = self
67 .last_name
68 .chars()
69 .filter(|c| c.is_ascii_alphabetic())
70 .take(5)
71 .collect();
72 format!("{}{}{:03}", first_initial, last_part.to_uppercase(), index)
73 }
74
75 pub fn to_email(&self, domain: &str) -> String {
77 let first = self
78 .first_name
79 .chars()
80 .filter(|c| c.is_ascii_alphabetic())
81 .collect::<String>()
82 .to_lowercase();
83 let last = self
84 .last_name
85 .chars()
86 .filter(|c| c.is_ascii_alphabetic())
87 .collect::<String>()
88 .to_lowercase();
89 format!("{}.{}@{}", first, last, domain)
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct NamePool {
96 pub first_names_male: Vec<&'static str>,
98 pub first_names_female: Vec<&'static str>,
100 pub last_names: Vec<&'static str>,
102 pub culture: NameCulture,
104}
105
106impl NamePool {
107 pub fn western_us() -> Self {
109 Self {
110 culture: NameCulture::WesternUs,
111 first_names_male: vec![
112 "James",
113 "John",
114 "Robert",
115 "Michael",
116 "William",
117 "David",
118 "Richard",
119 "Joseph",
120 "Thomas",
121 "Christopher",
122 "Charles",
123 "Daniel",
124 "Matthew",
125 "Anthony",
126 "Mark",
127 "Donald",
128 "Steven",
129 "Paul",
130 "Andrew",
131 "Joshua",
132 "Kenneth",
133 "Kevin",
134 "Brian",
135 "George",
136 "Timothy",
137 "Ronald",
138 "Edward",
139 "Jason",
140 "Jeffrey",
141 "Ryan",
142 "Jacob",
143 "Gary",
144 "Nicholas",
145 "Eric",
146 "Jonathan",
147 "Stephen",
148 "Larry",
149 "Justin",
150 "Scott",
151 "Brandon",
152 "Benjamin",
153 "Samuel",
154 "Raymond",
155 "Gregory",
156 "Frank",
157 "Alexander",
158 "Patrick",
159 "Jack",
160 "Dennis",
161 "Jerry",
162 "Tyler",
163 "Aaron",
164 "Jose",
165 "Adam",
166 "Nathan",
167 ],
168 first_names_female: vec![
169 "Mary",
170 "Patricia",
171 "Jennifer",
172 "Linda",
173 "Barbara",
174 "Elizabeth",
175 "Susan",
176 "Jessica",
177 "Sarah",
178 "Karen",
179 "Lisa",
180 "Nancy",
181 "Betty",
182 "Margaret",
183 "Sandra",
184 "Ashley",
185 "Kimberly",
186 "Emily",
187 "Donna",
188 "Michelle",
189 "Dorothy",
190 "Carol",
191 "Amanda",
192 "Melissa",
193 "Deborah",
194 "Stephanie",
195 "Rebecca",
196 "Sharon",
197 "Laura",
198 "Cynthia",
199 "Kathleen",
200 "Amy",
201 "Angela",
202 "Shirley",
203 "Anna",
204 "Brenda",
205 "Pamela",
206 "Emma",
207 "Nicole",
208 "Helen",
209 "Samantha",
210 "Katherine",
211 "Christine",
212 "Debra",
213 "Rachel",
214 "Carolyn",
215 "Janet",
216 "Catherine",
217 "Maria",
218 "Heather",
219 "Diane",
220 "Ruth",
221 "Julie",
222 ],
223 last_names: vec![
224 "Smith",
225 "Johnson",
226 "Williams",
227 "Brown",
228 "Jones",
229 "Garcia",
230 "Miller",
231 "Davis",
232 "Rodriguez",
233 "Martinez",
234 "Hernandez",
235 "Lopez",
236 "Gonzalez",
237 "Wilson",
238 "Anderson",
239 "Thomas",
240 "Taylor",
241 "Moore",
242 "Jackson",
243 "Martin",
244 "Lee",
245 "Perez",
246 "Thompson",
247 "White",
248 "Harris",
249 "Sanchez",
250 "Clark",
251 "Ramirez",
252 "Lewis",
253 "Robinson",
254 "Walker",
255 "Young",
256 "Allen",
257 "King",
258 "Wright",
259 "Scott",
260 "Torres",
261 "Nguyen",
262 "Hill",
263 "Flores",
264 "Green",
265 "Adams",
266 "Nelson",
267 "Baker",
268 "Hall",
269 "Rivera",
270 "Campbell",
271 "Mitchell",
272 "Carter",
273 "Roberts",
274 "Turner",
275 "Phillips",
276 "Evans",
277 "Parker",
278 "Edwards",
279 "Collins",
280 ],
281 }
282 }
283
284 pub fn german() -> Self {
286 Self {
287 culture: NameCulture::German,
288 first_names_male: vec![
289 "Hans",
290 "Klaus",
291 "Wolfgang",
292 "Dieter",
293 "Jürgen",
294 "Peter",
295 "Michael",
296 "Thomas",
297 "Andreas",
298 "Stefan",
299 "Markus",
300 "Christian",
301 "Martin",
302 "Frank",
303 "Bernd",
304 "Uwe",
305 "Ralf",
306 "Werner",
307 "Heinz",
308 "Helmut",
309 "Gerhard",
310 "Manfred",
311 "Horst",
312 "Karl",
313 "Heinrich",
314 "Friedrich",
315 "Wilhelm",
316 "Otto",
317 "Matthias",
318 "Tobias",
319 "Sebastian",
320 "Florian",
321 "Alexander",
322 "Maximilian",
323 "Felix",
324 "Lukas",
325 "Jonas",
326 "Leon",
327 "Paul",
328 "Philipp",
329 "Tim",
330 "Jan",
331 "Nico",
332 "Erik",
333 "Lars",
334 "Sven",
335 "Kai",
336 "Olaf",
337 "Rainer",
338 ],
339 first_names_female: vec![
340 "Anna",
341 "Maria",
342 "Elisabeth",
343 "Ursula",
344 "Helga",
345 "Monika",
346 "Petra",
347 "Sabine",
348 "Karin",
349 "Renate",
350 "Ingrid",
351 "Brigitte",
352 "Gisela",
353 "Erika",
354 "Christa",
355 "Andrea",
356 "Claudia",
357 "Susanne",
358 "Birgit",
359 "Heike",
360 "Martina",
361 "Nicole",
362 "Stefanie",
363 "Julia",
364 "Katharina",
365 "Christina",
366 "Sandra",
367 "Melanie",
368 "Daniela",
369 "Anja",
370 "Tanja",
371 "Simone",
372 "Silke",
373 "Nadine",
374 "Yvonne",
375 "Manuela",
376 "Sonja",
377 "Michaela",
378 "Angelika",
379 "Barbara",
380 "Gabriele",
381 "Beate",
382 "Doris",
383 "Eva",
384 "Franziska",
385 "Lena",
386 "Hannah",
387 "Sophie",
388 "Leonie",
389 "Laura",
390 ],
391 last_names: vec![
392 "Müller",
393 "Schmidt",
394 "Schneider",
395 "Fischer",
396 "Weber",
397 "Meyer",
398 "Wagner",
399 "Becker",
400 "Schulz",
401 "Hoffmann",
402 "Schäfer",
403 "Koch",
404 "Bauer",
405 "Richter",
406 "Klein",
407 "Wolf",
408 "Schröder",
409 "Neumann",
410 "Schwarz",
411 "Zimmermann",
412 "Braun",
413 "Krüger",
414 "Hofmann",
415 "Hartmann",
416 "Lange",
417 "Schmitt",
418 "Werner",
419 "Schmitz",
420 "Krause",
421 "Meier",
422 "Lehmann",
423 "Schmid",
424 "Schulze",
425 "Maier",
426 "Köhler",
427 "Herrmann",
428 "König",
429 "Walter",
430 "Mayer",
431 "Huber",
432 "Kaiser",
433 "Fuchs",
434 "Peters",
435 "Lang",
436 "Scholz",
437 "Möller",
438 "Weiß",
439 "Jung",
440 "Hahn",
441 "Schubert",
442 ],
443 }
444 }
445
446 pub fn french() -> Self {
448 Self {
449 culture: NameCulture::French,
450 first_names_male: vec![
451 "Jean",
452 "Pierre",
453 "Michel",
454 "André",
455 "Philippe",
456 "Jacques",
457 "Bernard",
458 "Alain",
459 "François",
460 "Robert",
461 "Marcel",
462 "René",
463 "Louis",
464 "Claude",
465 "Daniel",
466 "Yves",
467 "Christian",
468 "Patrick",
469 "Nicolas",
470 "Julien",
471 "Thomas",
472 "Antoine",
473 "Alexandre",
474 "Maxime",
475 "Lucas",
476 "Hugo",
477 "Théo",
478 "Mathieu",
479 "Guillaume",
480 "Laurent",
481 "Olivier",
482 "Christophe",
483 "Sébastien",
484 "Frédéric",
485 "Vincent",
486 "David",
487 "Eric",
488 "Pascal",
489 "Gilles",
490 "Thierry",
491 "Stéphane",
492 "Bruno",
493 "Dominique",
494 "Serge",
495 "Maurice",
496 "Henri",
497 "Paul",
498 "Charles",
499 "Emmanuel",
500 "Raphaël",
501 ],
502 first_names_female: vec![
503 "Marie",
504 "Jeanne",
505 "Françoise",
506 "Monique",
507 "Catherine",
508 "Nathalie",
509 "Isabelle",
510 "Sylvie",
511 "Anne",
512 "Martine",
513 "Nicole",
514 "Christine",
515 "Sophie",
516 "Valérie",
517 "Julie",
518 "Camille",
519 "Léa",
520 "Manon",
521 "Emma",
522 "Chloé",
523 "Inès",
524 "Sarah",
525 "Laura",
526 "Louise",
527 "Jade",
528 "Alice",
529 "Lola",
530 "Margot",
531 "Charlotte",
532 "Clara",
533 "Pauline",
534 "Marine",
535 "Aurélie",
536 "Céline",
537 "Sandrine",
538 "Virginie",
539 "Stéphanie",
540 "Élodie",
541 "Delphine",
542 "Laurence",
543 "Brigitte",
544 "Jacqueline",
545 "Simone",
546 "Denise",
547 "Madeleine",
548 "Thérèse",
549 "Hélène",
550 "Élise",
551 "Juliette",
552 "Marguerite",
553 ],
554 last_names: vec![
555 "Martin",
556 "Bernard",
557 "Dubois",
558 "Thomas",
559 "Robert",
560 "Richard",
561 "Petit",
562 "Durand",
563 "Leroy",
564 "Moreau",
565 "Simon",
566 "Laurent",
567 "Lefebvre",
568 "Michel",
569 "Garcia",
570 "David",
571 "Bertrand",
572 "Roux",
573 "Vincent",
574 "Fournier",
575 "Morel",
576 "Girard",
577 "André",
578 "Lefèvre",
579 "Mercier",
580 "Dupont",
581 "Lambert",
582 "Bonnet",
583 "François",
584 "Martinez",
585 "Legrand",
586 "Garnier",
587 "Faure",
588 "Rousseau",
589 "Blanc",
590 "Guérin",
591 "Muller",
592 "Henry",
593 "Roussel",
594 "Nicolas",
595 "Perrin",
596 "Morin",
597 "Mathieu",
598 "Clément",
599 "Gauthier",
600 "Dumont",
601 "Lopez",
602 "Fontaine",
603 "Chevalier",
604 "Robin",
605 ],
606 }
607 }
608
609 pub fn chinese() -> Self {
611 Self {
612 culture: NameCulture::Chinese,
613 first_names_male: vec![
614 "Wei", "Fang", "Lei", "Jun", "Jian", "Hao", "Chen", "Yang", "Ming", "Tao", "Long",
615 "Feng", "Bin", "Qiang", "Gang", "Hui", "Peng", "Xiang", "Bo", "Chao", "Dong",
616 "Liang", "Ning", "Kai", "Jie", "Yong", "Hai", "Lin", "Wen", "Zheng", "Hong", "Xin",
617 "Da", "Zhi", "Guang", "Cheng", "Yi", "Sheng", "Biao", "Ping", "Yun", "Song",
618 "Chang", "Kang", "Rui", "Nan", "Jia", "Xiao", "Yu", "Hua",
619 ],
620 first_names_female: vec![
621 "Fang", "Min", "Jing", "Li", "Yan", "Hong", "Juan", "Mei", "Ying", "Xia", "Hui",
622 "Lin", "Ling", "Ping", "Dan", "Yun", "Na", "Qian", "Xin", "Ya", "Wei", "Wen",
623 "Jie", "Qing", "Yu", "Hua", "Yue", "Xue", "Lan", "Zhen", "Rong", "Shu", "Fei",
624 "Lei", "Shan", "Ting", "Ni", "Ying", "Chen", "Huan", "Lu", "Ai", "Xiao", "Xiang",
625 "Yao", "Meng", "Qi", "Jun", "Bei", "Zhi",
626 ],
627 last_names: vec![
628 "Wang", "Li", "Zhang", "Liu", "Chen", "Yang", "Huang", "Zhao", "Wu", "Zhou", "Xu",
629 "Sun", "Ma", "Zhu", "Hu", "Guo", "He", "Lin", "Gao", "Luo", "Zheng", "Liang",
630 "Xie", "Tang", "Song", "Xu", "Han", "Deng", "Feng", "Cao", "Peng", "Xiao", "Cheng",
631 "Yuan", "Tian", "Dong", "Pan", "Cai", "Jiang", "Wei", "Yu", "Du", "Ye", "Shi",
632 "Lu", "Shen", "Su", "Jia", "Fan", "Jin",
633 ],
634 }
635 }
636
637 pub fn japanese() -> Self {
639 Self {
640 culture: NameCulture::Japanese,
641 first_names_male: vec![
642 "Hiroshi", "Takeshi", "Kenji", "Yuki", "Kazuki", "Ryota", "Daiki", "Shota", "Yuto",
643 "Kenta", "Haruto", "Sota", "Riku", "Yuma", "Kaito", "Ren", "Hayato", "Takumi",
644 "Kouki", "Ryuu", "Naoki", "Tsubasa", "Yuuki", "Akira", "Satoshi", "Makoto",
645 "Tetsuya", "Masaki", "Shin", "Kei", "Daisuke", "Shunsuke", "Tomoya", "Yusuke",
646 "Tatsuya", "Katsuki", "Shun", "Yamato", "Koji", "Hideo", "Takahiro", "Noboru",
647 "Shinji", "Osamu", "Minoru", "Hideki", "Jun", "Masaru", "Ken", "Ryo",
648 ],
649 first_names_female: vec![
650 "Yuki", "Sakura", "Hana", "Yui", "Mio", "Rin", "Aoi", "Mei", "Saki", "Miku",
651 "Nanami", "Ayaka", "Misaki", "Haruka", "Momoka", "Rina", "Yuna", "Hinata",
652 "Koharu", "Miyu", "Akari", "Hikari", "Kaede", "Natsuki", "Mai", "Ami", "Aya",
653 "Emi", "Kana", "Megumi", "Tomoko", "Yoko", "Keiko", "Naomi", "Mayumi", "Chika",
654 "Nana", "Risa", "Asuka", "Fumiko", "Kyoko", "Reiko", "Noriko", "Sachiko", "Mariko",
655 "Shiori", "Midori", "Kanako", "Minami", "Eriko",
656 ],
657 last_names: vec![
658 "Sato",
659 "Suzuki",
660 "Takahashi",
661 "Tanaka",
662 "Watanabe",
663 "Ito",
664 "Yamamoto",
665 "Nakamura",
666 "Kobayashi",
667 "Kato",
668 "Yoshida",
669 "Yamada",
670 "Sasaki",
671 "Yamaguchi",
672 "Matsumoto",
673 "Inoue",
674 "Kimura",
675 "Hayashi",
676 "Shimizu",
677 "Yamazaki",
678 "Mori",
679 "Abe",
680 "Ikeda",
681 "Hashimoto",
682 "Yamashita",
683 "Ishikawa",
684 "Nakajima",
685 "Maeda",
686 "Fujita",
687 "Ogawa",
688 "Goto",
689 "Okada",
690 "Hasegawa",
691 "Murakami",
692 "Kondo",
693 "Ishii",
694 "Saito",
695 "Sakamoto",
696 "Endo",
697 "Aoki",
698 "Fujii",
699 "Nishimura",
700 "Fukuda",
701 "Ota",
702 "Miura",
703 "Okamoto",
704 "Matsuda",
705 "Nakagawa",
706 "Fujiwara",
707 "Kawamura",
708 ],
709 }
710 }
711
712 pub fn indian() -> Self {
714 Self {
715 culture: NameCulture::Indian,
716 first_names_male: vec![
717 "Raj", "Amit", "Vikram", "Rahul", "Sanjay", "Arun", "Suresh", "Rajesh", "Deepak",
718 "Vijay", "Prakash", "Manoj", "Sunil", "Anil", "Ravi", "Ashok", "Ramesh", "Mukesh",
719 "Sandeep", "Ajay", "Naveen", "Pradeep", "Sachin", "Nitin", "Vinod", "Rakesh",
720 "Srinivas", "Ganesh", "Krishna", "Mohan", "Kiran", "Venkat", "Hari", "Shankar",
721 "Dinesh", "Mahesh", "Satish", "Girish", "Naresh", "Harish", "Pavan", "Arjun",
722 "Anand", "Vivek", "Rohit", "Gaurav", "Kunal", "Vishal", "Akhil", "Dev",
723 ],
724 first_names_female: vec![
725 "Priya",
726 "Anita",
727 "Sunita",
728 "Kavita",
729 "Neha",
730 "Pooja",
731 "Anjali",
732 "Deepa",
733 "Meena",
734 "Rekha",
735 "Lakshmi",
736 "Padma",
737 "Gita",
738 "Sita",
739 "Rani",
740 "Devi",
741 "Shalini",
742 "Nisha",
743 "Swati",
744 "Preeti",
745 "Divya",
746 "Shreya",
747 "Aishwarya",
748 "Ritu",
749 "Seema",
750 "Jyoti",
751 "Shweta",
752 "Pallavi",
753 "Rashmi",
754 "Smita",
755 "Varsha",
756 "Archana",
757 "Asha",
758 "Manisha",
759 "Usha",
760 "Vandana",
761 "Geeta",
762 "Rita",
763 "Maya",
764 "Radha",
765 "Sapna",
766 "Megha",
767 "Nikita",
768 "Tanvi",
769 "Aditi",
770 "Bhavna",
771 "Chitra",
772 "Komal",
773 "Madhuri",
774 "Parul",
775 ],
776 last_names: vec![
777 "Sharma",
778 "Patel",
779 "Singh",
780 "Kumar",
781 "Gupta",
782 "Joshi",
783 "Verma",
784 "Shah",
785 "Mehta",
786 "Reddy",
787 "Rao",
788 "Iyer",
789 "Nair",
790 "Menon",
791 "Pillai",
792 "Das",
793 "Bose",
794 "Sen",
795 "Chatterjee",
796 "Banerjee",
797 "Mukherjee",
798 "Ghosh",
799 "Roy",
800 "Dutta",
801 "Kapoor",
802 "Malhotra",
803 "Agarwal",
804 "Sinha",
805 "Thakur",
806 "Saxena",
807 "Mishra",
808 "Pandey",
809 "Trivedi",
810 "Desai",
811 "Modi",
812 "Kulkarni",
813 "Patil",
814 "Kaur",
815 "Chopra",
816 "Khanna",
817 "Bhatia",
818 "Choudhury",
819 "Rajan",
820 "Subramaniam",
821 "Venkatesh",
822 "Naidu",
823 "Hegde",
824 "Shukla",
825 "Prasad",
826 "Murthy",
827 ],
828 }
829 }
830
831 pub fn hispanic() -> Self {
833 Self {
834 culture: NameCulture::Hispanic,
835 first_names_male: vec![
836 "José",
837 "Juan",
838 "Carlos",
839 "Luis",
840 "Miguel",
841 "Francisco",
842 "Antonio",
843 "Manuel",
844 "Pedro",
845 "Javier",
846 "Fernando",
847 "Rafael",
848 "Alejandro",
849 "Ricardo",
850 "Eduardo",
851 "Roberto",
852 "Daniel",
853 "Pablo",
854 "Sergio",
855 "Jorge",
856 "Andrés",
857 "Raúl",
858 "Diego",
859 "Enrique",
860 "Óscar",
861 "Adrián",
862 "Víctor",
863 "Martín",
864 "Gabriel",
865 "Álvaro",
866 "Iván",
867 "Mario",
868 "César",
869 "Héctor",
870 "Alberto",
871 "Gustavo",
872 "Arturo",
873 "Ramón",
874 "Hugo",
875 "Salvador",
876 "Ernesto",
877 "Guillermo",
878 "Ignacio",
879 "Jaime",
880 "Felipe",
881 "Tomás",
882 "Santiago",
883 "Mateo",
884 "Sebastián",
885 "Nicolás",
886 ],
887 first_names_female: vec![
888 "María",
889 "Carmen",
890 "Ana",
891 "Laura",
892 "Rosa",
893 "Isabel",
894 "Elena",
895 "Patricia",
896 "Teresa",
897 "Lucia",
898 "Pilar",
899 "Dolores",
900 "Paula",
901 "Sara",
902 "Marta",
903 "Julia",
904 "Cristina",
905 "Claudia",
906 "Andrea",
907 "Mónica",
908 "Gloria",
909 "Beatriz",
910 "Alicia",
911 "Rocío",
912 "Victoria",
913 "Silvia",
914 "Eva",
915 "Raquel",
916 "Adriana",
917 "Lorena",
918 "Gabriela",
919 "Marina",
920 "Sandra",
921 "Verónica",
922 "Natalia",
923 "Carolina",
924 "Diana",
925 "Alejandra",
926 "Cecilia",
927 "Daniela",
928 "Sofía",
929 "Valentina",
930 "Camila",
931 "Isabella",
932 "Mariana",
933 "Fernanda",
934 "Paola",
935 "Liliana",
936 "Angela",
937 "Inés",
938 ],
939 last_names: vec![
940 "García",
941 "Rodríguez",
942 "Martínez",
943 "López",
944 "González",
945 "Hernández",
946 "Pérez",
947 "Sánchez",
948 "Ramírez",
949 "Torres",
950 "Flores",
951 "Rivera",
952 "Gómez",
953 "Díaz",
954 "Reyes",
955 "Cruz",
956 "Morales",
957 "Ortiz",
958 "Gutiérrez",
959 "Chávez",
960 "Ramos",
961 "Vargas",
962 "Castillo",
963 "Jiménez",
964 "Moreno",
965 "Romero",
966 "Herrera",
967 "Medina",
968 "Aguilar",
969 "Vega",
970 "Castro",
971 "Mendoza",
972 "Ruiz",
973 "Fernández",
974 "Álvarez",
975 "Muñoz",
976 "Rojas",
977 "Silva",
978 "Suárez",
979 "Delgado",
980 "Navarro",
981 "Santos",
982 "Molina",
983 "Espinoza",
984 "Guerrero",
985 "Cabrera",
986 "Campos",
987 "Cortés",
988 "Salazar",
989 "Luna",
990 ],
991 }
992 }
993
994 pub fn for_culture(culture: NameCulture) -> Self {
996 match culture {
997 NameCulture::WesternUs => Self::western_us(),
998 NameCulture::German => Self::german(),
999 NameCulture::French => Self::french(),
1000 NameCulture::Chinese => Self::chinese(),
1001 NameCulture::Japanese => Self::japanese(),
1002 NameCulture::Indian => Self::indian(),
1003 NameCulture::Hispanic => Self::hispanic(),
1004 }
1005 }
1006
1007 pub fn generate_name(&self, rng: &mut impl Rng) -> PersonName {
1009 let is_male = rng.gen_bool(0.5);
1010
1011 let first_name = if is_male {
1012 self.first_names_male.choose(rng).unwrap()
1013 } else {
1014 self.first_names_female.choose(rng).unwrap()
1015 };
1016
1017 let last_name = self.last_names.choose(rng).unwrap();
1018
1019 PersonName {
1020 first_name: (*first_name).to_string(),
1021 last_name: (*last_name).to_string(),
1022 display_name: format!("{} {}", first_name, last_name),
1023 culture: self.culture,
1024 is_male,
1025 }
1026 }
1027}
1028
1029#[derive(Debug, Clone)]
1031pub struct MultiCultureNameGenerator {
1032 pools: HashMap<NameCulture, NamePool>,
1033 distribution: Vec<(NameCulture, f64)>,
1034 email_domain: String,
1035}
1036
1037impl Default for MultiCultureNameGenerator {
1038 fn default() -> Self {
1039 Self::new()
1040 }
1041}
1042
1043impl MultiCultureNameGenerator {
1044 pub fn new() -> Self {
1046 let mut pools = HashMap::new();
1047 for culture in NameCulture::all() {
1048 pools.insert(*culture, NamePool::for_culture(*culture));
1049 }
1050
1051 Self {
1052 pools,
1053 distribution: vec![
1054 (NameCulture::WesternUs, 0.40),
1055 (NameCulture::Hispanic, 0.20),
1056 (NameCulture::German, 0.10),
1057 (NameCulture::French, 0.05),
1058 (NameCulture::Chinese, 0.10),
1059 (NameCulture::Japanese, 0.05),
1060 (NameCulture::Indian, 0.10),
1061 ],
1062 email_domain: "company.com".to_string(),
1063 }
1064 }
1065
1066 pub fn with_distribution(distribution: Vec<(NameCulture, f64)>) -> Self {
1068 let mut gen = Self::new();
1069 gen.distribution = distribution;
1070 gen
1071 }
1072
1073 pub fn with_email_domain(mut self, domain: &str) -> Self {
1075 self.email_domain = domain.to_string();
1076 self
1077 }
1078
1079 pub fn set_email_domain(&mut self, domain: &str) {
1081 self.email_domain = domain.to_string();
1082 }
1083
1084 pub fn email_domain(&self) -> &str {
1086 &self.email_domain
1087 }
1088
1089 fn select_culture(&self, rng: &mut impl Rng) -> NameCulture {
1091 let roll: f64 = rng.gen();
1092 let mut cumulative = 0.0;
1093
1094 for (culture, weight) in &self.distribution {
1095 cumulative += weight;
1096 if roll < cumulative {
1097 return *culture;
1098 }
1099 }
1100
1101 NameCulture::WesternUs
1103 }
1104
1105 pub fn generate_name(&self, rng: &mut impl Rng) -> PersonName {
1107 let culture = self.select_culture(rng);
1108 self.pools
1109 .get(&culture)
1110 .map(|pool| pool.generate_name(rng))
1111 .unwrap_or_else(|| NamePool::western_us().generate_name(rng))
1112 }
1113
1114 pub fn generate_name_for_culture(
1116 &self,
1117 culture: NameCulture,
1118 rng: &mut impl Rng,
1119 ) -> PersonName {
1120 self.pools
1121 .get(&culture)
1122 .map(|pool| pool.generate_name(rng))
1123 .unwrap_or_else(|| NamePool::western_us().generate_name(rng))
1124 }
1125
1126 pub fn generate_user_id(&self, name: &PersonName, index: usize) -> String {
1128 name.to_user_id(index)
1129 }
1130
1131 pub fn generate_email(&self, name: &PersonName) -> String {
1133 name.to_email(&self.email_domain)
1134 }
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139 use super::*;
1140 use rand::SeedableRng;
1141 use rand_chacha::ChaCha8Rng;
1142
1143 #[test]
1144 fn test_name_pool_generation() {
1145 let mut rng = ChaCha8Rng::seed_from_u64(42);
1146 let pool = NamePool::western_us();
1147 let name = pool.generate_name(&mut rng);
1148
1149 assert!(!name.first_name.is_empty());
1150 assert!(!name.last_name.is_empty());
1151 assert_eq!(name.culture, NameCulture::WesternUs);
1152 }
1153
1154 #[test]
1155 fn test_multi_culture_generator() {
1156 let mut rng = ChaCha8Rng::seed_from_u64(42);
1157 let generator = MultiCultureNameGenerator::new();
1158
1159 let mut culture_counts: HashMap<NameCulture, usize> = HashMap::new();
1161 for _ in 0..100 {
1162 let name = generator.generate_name(&mut rng);
1163 *culture_counts.entry(name.culture).or_insert(0) += 1;
1164 }
1165
1166 assert!(culture_counts.len() > 1);
1168 }
1169
1170 #[test]
1171 fn test_user_id_generation() {
1172 let name = PersonName {
1173 first_name: "John".to_string(),
1174 last_name: "Smith".to_string(),
1175 display_name: "John Smith".to_string(),
1176 culture: NameCulture::WesternUs,
1177 is_male: true,
1178 };
1179
1180 let user_id = name.to_user_id(42);
1181 assert_eq!(user_id, "JSMITH042");
1182 }
1183
1184 #[test]
1185 fn test_email_generation() {
1186 let name = PersonName {
1187 first_name: "María".to_string(),
1188 last_name: "García".to_string(),
1189 display_name: "María García".to_string(),
1190 culture: NameCulture::Hispanic,
1191 is_male: false,
1192 };
1193
1194 let email = name.to_email("acme.com");
1195 assert_eq!(email, "mara.garca@acme.com"); }
1197
1198 #[test]
1199 fn test_all_culture_pools() {
1200 let mut rng = ChaCha8Rng::seed_from_u64(42);
1201
1202 for culture in NameCulture::all() {
1203 let pool = NamePool::for_culture(*culture);
1204 let name = pool.generate_name(&mut rng);
1205 assert!(!name.first_name.is_empty());
1206 assert!(!name.last_name.is_empty());
1207 assert_eq!(name.culture, *culture);
1208 }
1209 }
1210}