1use crate::error::MissingArgumentError;
6use lazy_static::lazy_static;
7use rand::Rng;
8use regex::Regex;
9
10lazy_static! {
14 static ref EMAIL_REGEX: Regex = Regex::new(r"^[\w\-]+(\.[\w\-]+)*@([A-Za-z0-9-]+\.)+[A-Za-z]{2,4}$").unwrap();
16
17 static ref RANDOM_SOURCE: Vec<&'static str> = vec![
18 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "a", "b", "c", "d", "e", "f", "g",
19 "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
20 "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
21 "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
22 ];
23
24 static ref RANDOM_SOURCE_SPEC: Vec<&'static str> = vec![
25 "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "a", "b", "c", "d", "e", "f", "g",
26 "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y",
27 "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
28 "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "~", "`", "!", "@", "#", "$", "%", "^", "&",
29 "*", "(", ")", "-", "_", "=", "+", "[", "{", "]", "}", ";", ":", "'", "\"", ",", "<", ".",
30 ">", "/", "?", "\\"
31 ];
32
33 static ref KO_CONSONANTS: Vec<char> = vec![
38 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ',
39 'ㅌ', 'ㅍ', 'ㅎ',
40 ];
41
42 static ref KO_SEPARATED_CONSONANTS: Vec<Vec<char>> = vec![
44 vec!['ㄱ'],
45 vec!['ㄱ', 'ㄱ'],
46 vec!['ㄴ'],
47 vec!['ㄷ'],
48 vec!['ㄷ', 'ㄷ'],
49 vec!['ㄹ'],
50 vec!['ㅁ'],
51 vec!['ㅂ'],
52 vec!['ㅂ', 'ㅂ'],
53 vec!['ㅅ'],
54 vec!['ㅅ', 'ㅅ'],
55 vec!['ㅇ'],
56 vec!['ㅈ'],
57 vec!['ㅈ', 'ㅈ'],
58 vec!['ㅊ'],
59 vec!['ㅋ'],
60 vec!['ㅌ'],
61 vec!['ㅍ'],
62 vec!['ㅎ'],
63 ];
64
65 static ref KO_VOWELS: Vec<char> = vec![
67 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ',
68 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ',
69 ];
70
71 static ref KO_SEPARATED_VOWELS: Vec<Vec<char>> = vec![
73 vec!['ㅏ'],
74 vec!['ㅐ'],
75 vec!['ㅑ'],
76 vec!['ㅒ'],
77 vec!['ㅓ'],
78 vec!['ㅔ'],
79 vec!['ㅕ'],
80 vec!['ㅖ'],
81 vec!['ㅗ'],
82 vec!['ㅗ', 'ㅏ'],
83 vec!['ㅗ', 'ㅐ'],
84 vec!['ㅗ', 'ㅣ'],
85 vec!['ㅛ'],
86 vec!['ㅜ'],
87 vec!['ㅜ', 'ㅓ'],
88 vec!['ㅜ', 'ㅔ'],
89 vec!['ㅜ', 'ㅣ'],
90 vec!['ㅠ'],
91 vec!['ㅡ'],
92 vec!['ㅡ', 'ㅣ'],
93 vec!['ㅣ'],
94 ];
95
96 static ref KO_FINAL_CONSONANTS: Vec<char> = vec![
98 0 as char, 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ',
99 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ',
100 ];
101
102 static ref KO_SEPARATED_FINAL_CONSONANTS: Vec<Vec<char>> = vec![
104 vec![],
105 vec!['ㄱ'],
106 vec!['ㄱ', 'ㄱ'],
107 vec!['ㄱ', 'ㅅ'],
108 vec!['ㄴ'],
109 vec!['ㄴ', 'ㅈ'],
110 vec!['ㄴ', 'ㅎ'],
111 vec!['ㄷ'],
112 vec!['ㄹ'],
113 vec!['ㄹ', 'ㄱ'],
114 vec!['ㄹ', 'ㅁ'],
115 vec!['ㄹ', 'ㅂ'],
116 vec!['ㄹ', 'ㅅ'],
117 vec!['ㄹ', 'ㅌ'],
118 vec!['ㄹ', 'ㅍ'],
119 vec!['ㄹ', 'ㅎ'],
120 vec!['ㅁ'],
121 vec!['ㅂ'],
122 vec!['ㅂ', 'ㅅ'],
123 vec!['ㅅ'],
124 vec!['ㅅ', 'ㅅ'],
125 vec!['ㅇ'],
126 vec!['ㅈ'],
127 vec!['ㅊ'],
128 vec!['ㅋ'],
129 vec!['ㅌ'],
130 vec!['ㅍ'],
131 vec!['ㅎ'],
132 ];
133
134 static ref KO_SEPARATED_FORTES_VOWELS: Vec<Vec<char>> = vec![
136 vec!['ㄱ'],
137 vec!['ㄱ', 'ㄱ'],
138 vec!['ㄱ', 'ㅅ'],
139 vec!['ㄴ'],
140 vec!['ㄴ', 'ㅈ'],
141 vec!['ㄴ', 'ㅎ'],
142 vec!['ㄷ'],
143 vec!['ㄸ'],
144 vec!['ㄹ'],
145 vec!['ㄹ', 'ㄱ'],
146 vec!['ㄹ', 'ㅁ'],
147 vec!['ㄹ', 'ㅂ'],
148 vec!['ㄹ', 'ㅅ'],
149 vec!['ㄹ', 'ㄷ'],
150 vec!['ㄹ', 'ㅍ'],
151 vec!['ㄹ', 'ㅎ'],
152 vec!['ㅁ'],
153 vec!['ㅂ'],
154 vec!['ㅂ', 'ㅂ'],
155 vec!['ㅂ', 'ㅅ'],
156 vec!['ㅅ'],
157 vec!['ㅅ', 'ㅅ'],
158 vec!['ㅇ'],
159 vec!['ㅈ'],
160 vec!['ㅈ', 'ㅈ'],
161 vec!['ㅊ'],
162 vec!['ㅋ'],
163 vec!['ㅌ'],
164 vec!['ㅍ'],
165 vec!['ㅎ'],
166 vec!['ㅏ'],
167 vec!['ㅐ'],
168 vec!['ㅑ'],
169 vec!['ㅒ'],
170 vec!['ㅓ'],
171 vec!['ㅔ'],
172 vec!['ㅕ'],
173 vec!['ㅖ'],
174 vec!['ㅗ'],
175 vec!['ㅗ', 'ㅏ'],
176 vec!['ㅗ', 'ㅐ'],
177 vec!['ㅗ', 'ㅣ'],
178 vec!['ㅛ'],
179 vec!['ㅜ'],
180 vec!['ㅜ', 'ㅓ'],
181 vec!['ㅜ', 'ㅔ'],
182 vec!['ㅜ', 'ㅣ'],
183 vec!['ㅠ'],
184 vec!['ㅡ'],
185 vec!['ㅡ', 'ㅣ'],
186 vec!['ㅣ'],
187 ];
188}
189
190pub fn validate_email(target: Option<&str>) -> Result<bool, MissingArgumentError> {
194 match target {
196 None => Err(MissingArgumentError::default()),
197 Some(v) => Ok(EMAIL_REGEX.is_match(v)),
198 }
199}
200
201pub fn extract_initial_consonant(target: Option<&str>) -> Result<String, MissingArgumentError> {
222 match target {
223 None => Err(MissingArgumentError::default()),
224 Some(v) => {
225 let result = {
226 let mut temp = String::with_capacity(v.chars().count()); for (_, t) in v.chars().enumerate() {
229 if t >= '가' && t <= '힣' {
230 temp += KO_CONSONANTS[(((t as u32) - ('가' as u32)) / 588) as usize]
231 .to_string()
232 .as_str();
233 } else {
234 temp += t.to_string().as_str();
235 }
236 }
237
238 temp
239 };
240
241 Ok(result)
242 }
243 }
244}
245
246pub fn separate_simple_consonant_vowel(
277 target: Option<&str>,
278) -> Result<String, MissingArgumentError> {
279 match target {
280 None => Err(MissingArgumentError::default()),
281 Some(v) => {
282 let result = {
283 let mut temp = String::with_capacity(v.chars().count() * 3); let mut consonant: u32;
285 let start = '가' as u32;
286
287 for (_, t) in v.chars().enumerate() {
288 if t >= '가' && t <= '힣' {
289 consonant = (t as u32) - start;
290
291 temp += KO_CONSONANTS[(consonant / 588) as usize]
293 .to_string()
294 .as_str();
295 consonant = consonant % 588;
296
297 temp += KO_VOWELS[(consonant / 28) as usize].to_string().as_str();
299 consonant = consonant % 28;
300
301 if consonant != 0 {
302 temp += KO_FINAL_CONSONANTS[consonant as usize].to_string().as_str();
304 }
305 } else {
306 temp += t.to_string().as_str();
307 }
308 }
309
310 temp
311 };
312
313 Ok(result)
314 }
315 }
316}
317
318pub fn separate_consonant_vowel_completely(
346 target: Option<&str>,
347) -> Result<String, MissingArgumentError> {
348 match target {
349 None => Err(MissingArgumentError::default()),
350 Some(v) => {
351 let result = {
354 let mut temp = String::with_capacity(v.chars().count() * 6);
355 let mut consonant: u32;
356 let start = '가' as u32;
357
358 for (_, t) in v.chars().enumerate() {
359 if t >= '가' && t <= '힣' {
360 consonant = (t as u32) - start;
361
362 KO_SEPARATED_CONSONANTS[(consonant / 588) as usize]
364 .iter()
365 .for_each(|m| {
366 temp += m.to_string().as_str();
367 });
368
369 consonant %= 588;
370
371 KO_SEPARATED_VOWELS[(consonant / 28) as usize]
373 .iter()
374 .for_each(|m| {
375 temp += m.to_string().as_str();
376 });
377
378 consonant %= 28;
379
380 if consonant != 0 {
381 KO_SEPARATED_FINAL_CONSONANTS[consonant as usize]
383 .iter()
384 .for_each(|m| {
385 temp += m.to_string().as_str();
386 });
387 }
388 } else if t >= 'ㄱ' && t <= 'ㅣ' {
389 KO_SEPARATED_FORTES_VOWELS[((t as u32) - ('ㄱ' as u32)) as usize]
394 .iter()
395 .for_each(|m| {
396 temp += m.to_string().as_str();
397 })
398 } else {
399 temp += t.to_string().as_str();
400 }
401 }
402
403 temp
404 };
405
406 Ok(result)
407 }
408 }
409}
410
411pub fn to_hex(target: Option<&[u8]>, to_uppercase: bool) -> Option<String> {
422 if target.is_none() {
423 return None;
424 }
425
426 let v: Vec<String> = target
427 .unwrap()
428 .iter()
429 .map(|b| {
430 if to_uppercase {
431 format!("{:02X}", b)
432 } else {
433 format!("{:02x}", b)
434 }
435 })
436 .collect();
437
438 return Some(v.join(""));
439}
440
441pub fn generate_random_string(length: u32) -> Option<String> {
453 let mut random = rand::thread_rng();
454 let mut count: u32 = 0;
455 let mut result: Vec<&str> = vec![];
456 let source_size = RANDOM_SOURCE.len() - 1;
457
458 while count < length {
459 let index = random.gen_range(0..=source_size);
460
461 result.push(RANDOM_SOURCE.get(index).unwrap());
462
463 count += 1;
464 }
465
466 Some(result.join(""))
467}
468
469pub fn generate_random_string_with_spec(length: u32) -> Option<String> {
481 let mut random = rand::thread_rng();
482 let mut count: u32 = 0;
483 let mut result: Vec<&str> = vec![];
484 let source_size = RANDOM_SOURCE_SPEC.len() - 1;
485
486 while count < length {
487 let index = random.gen_range(0..=source_size);
488
489 result.push(RANDOM_SOURCE_SPEC.get(index).unwrap());
490
491 count += 1;
492 }
493
494 Some(result.join(""))
495}
496
497#[cfg(test)]
498mod tests {
499 use super::*;
500
501 #[test]
502 fn validate_email_test() {
503 let mut email = "joonho.son@me.com";
504 let result = validate_email(Some(email));
505
506 assert!(!result.is_err());
507 assert!(
508 validate_email(Some(email)).unwrap(),
509 "정상적인 이메일 유효성 검사 실패"
510 );
511
512 email = "test@test";
513
514 assert!(!validate_email(Some(email)).unwrap());
515
516 email = "test@test.";
517
518 assert!(!validate_email(Some(email)).unwrap());
519
520 email = "";
521
522 assert!(!validate_email(Some(email)).unwrap());
523
524 assert!(validate_email(None).is_err());
525
526 assert_eq!(
528 validate_email(None).unwrap_err(),
529 MissingArgumentError::default(),
530 "에러 불일치"
531 );
532 }
533
534 #[test]
535 #[should_panic]
536 fn invalid_email_should_panic_test() {
537 validate_email(None).unwrap();
538 }
539
540 #[test]
541 fn korean_domain_fail_test() {
542 let mut email = "한글ID@test.com";
543
544 assert!(
545 !validate_email(Some(email)).is_err(),
546 "한글 ID를 포함하는 이메일 검사 실패"
547 );
548
549 email = "test@한글도메인.com";
550
551 assert!(
552 !validate_email(Some(email)).is_err(),
553 "한글 도메인을 포함하는 이메일 검사 실패"
554 );
555
556 email = "홍길동@한글도메인.com";
557
558 assert!(
559 !validate_email(Some(email)).is_err(),
560 "한글 ID 및 한글 도메인을 포함하는 이메일 검사 실패"
561 );
562 }
563
564 #[test]
565 fn extract_initial_consonant_test() {
566 let mut target = "한글만 있습니다.";
567 let mut result = extract_initial_consonant(Some(target)).unwrap();
568
569 println!("extract result : {}", result);
570
571 assert_eq!(
572 "ㅎㄱㅁ ㅇㅅㄴㄷ.",
573 result.as_str(),
574 "한글만 있을 경우 초성 추출 실패"
575 );
576
577 target = "한글과 English가 함께 있습니다.";
578 result = extract_initial_consonant(Some(target)).unwrap();
579
580 println!("extract result : {}", result);
581
582 assert_eq!(
583 "ㅎㄱㄱ Englishㄱ ㅎㄲ ㅇㅅㄴㄷ.",
584 result.as_str(),
585 "한글과 영어가 혼재되어 있을 경우 추출 실패"
586 );
587
588 target = "세종대왕(世宗大王)";
589 result = extract_initial_consonant(Some(target)).unwrap();
590
591 println!("extract result : {}", result);
592
593 assert_eq!(
594 "ㅅㅈㄷㅇ(世宗大王)",
595 result.as_str(),
596 "한글과 한자가 혼재되어 있을 경우 추출 실패"
597 );
598
599 target = "이건 이모티콘(❤😑😊😂)을 포함합니다.";
600 result = extract_initial_consonant(Some(target)).unwrap();
601
602 println!("extract result : {}", result);
603
604 assert_eq!(
605 "ㅇㄱ ㅇㅁㅌㅋ(❤😑😊😂)ㅇ ㅍㅎㅎㄴㄷ.",
606 result.as_str(),
607 "한글과 이모티콘이 혼재되어 있을 경우 추출 실패"
608 );
609 }
610
611 #[test]
612 fn separate_consonant_vowel_test() {
613 let mut target = "한글만";
614 let mut result = separate_simple_consonant_vowel(Some(target)).unwrap();
615
616 println!("separate result : {}", result);
617
618 assert_eq!(
619 "ㅎㅏㄴㄱㅡㄹㅁㅏㄴ",
620 result.as_str(),
621 "한글만 있는 초/중/종성 분리 실패"
622 );
623
624 target = "한글과 English가 함께";
625 result = separate_simple_consonant_vowel(Some(target)).unwrap();
626
627 println!("separate result : {}", result);
628
629 assert_eq!(
630 "ㅎㅏㄴㄱㅡㄹㄱㅘ Englishㄱㅏ ㅎㅏㅁㄲㅔ",
631 result.as_str(),
632 "한글과 영어가 혼재되어 있을 경우 초/중/종성 분리 실패"
633 );
634
635 target = "맑음";
636 result = separate_simple_consonant_vowel(Some(target)).unwrap();
637
638 println!("separate result : {}", result);
639
640 assert_eq!(
641 "ㅁㅏㄺㅇㅡㅁ",
642 result.as_str(),
643 "겹받침이 있을 경우 초/중/종성 분리 실패"
644 );
645
646 target = "많이 주세요.";
647 result = separate_simple_consonant_vowel(Some(target)).unwrap();
648
649 println!("separate result : {}", result);
650
651 assert_eq!(
652 "ㅁㅏㄶㅇㅣ ㅈㅜㅅㅔㅇㅛ.",
653 result.as_str(),
654 "겹받침이 있을 경우 초/중/종성 분리 실패"
655 );
656
657 target = "꽊꽊이";
658 result = separate_simple_consonant_vowel(Some(target)).unwrap();
659
660 println!("separate result : {}", result);
661
662 assert_eq!("ㄲㅘㄲㄲㅘㄲㅇㅣ", result.as_str());
663 }
664
665 #[test]
666 fn separate_consonant_vowel_completely_test() {
667 let mut target = "한글만";
668 let mut result = separate_consonant_vowel_completely(Some(target)).unwrap();
669
670 println!("separate result : {}", result);
671
672 assert_eq!(
673 "ㅎㅏㄴㄱㅡㄹㅁㅏㄴ",
674 result.as_str(),
675 "한글만 있는 초/중/종성 분리 실패"
676 );
677
678 target = "꽊꽊이";
679 result = separate_consonant_vowel_completely(Some(target)).unwrap();
680
681 println!("separate result : {}", result);
682
683 assert_eq!(
684 "ㄱㄱㅗㅏㄱㄱㄱㄱㅗㅏㄱㄱㅇㅣ",
685 result.as_str(),
686 "쌍자음, 이중 모음이 있을 경우 분리 실패"
687 );
688
689 target = "꽊많이 줬으면 좋겠어요1❤❤.";
690 result = separate_consonant_vowel_completely(Some(target)).unwrap();
691
692 println!("separate result : {}", result);
693
694 assert_eq!(
695 "ㄱㄱㅗㅏㄱㄱㅁㅏㄴㅎㅇㅣ ㅈㅜㅓㅅㅅㅇㅡㅁㅕㄴ ㅈㅗㅎㄱㅔㅅㅅㅇㅓㅇㅛ1❤❤.",
696 result.as_str(),
697 "쌍자음, 이중모음이 있을 경우 분리 실패"
698 );
699
700 target =
701 r#""투표율을 40%(percentage) 초중반대는 충분히 되지 않을까 생각한다"며 말문을 뗐다."#;
702 result = separate_consonant_vowel_completely(Some(target)).unwrap();
703
704 println!("separate result : {}", result);
705
706 assert_eq!(
707 r#""ㅌㅜㅍㅛㅇㅠㄹㅇㅡㄹ 40%(percentage) ㅊㅗㅈㅜㅇㅂㅏㄴㄷㅐㄴㅡㄴ ㅊㅜㅇㅂㅜㄴㅎㅣ ㄷㅗㅣㅈㅣ ㅇㅏㄴㅎㅇㅡㄹㄱㄱㅏ ㅅㅐㅇㄱㅏㄱㅎㅏㄴㄷㅏ"ㅁㅕ ㅁㅏㄹㅁㅜㄴㅇㅡㄹ ㄷㄷㅔㅅㅅㄷㅏ."#,
708 result.as_str(),
709 "쌍자음, 이중모음, 특수 기호를 포함하는 경우 분리 실패"
710 );
711 }
712
713 #[test]
714 fn random_string_test() {
715 let length = 17;
716 let result = generate_random_string(length);
717
718 assert!(result.is_some());
719
720 let result = result.unwrap();
721
722 assert_eq!(length, result.len() as u32);
723
724 println!(
725 "--------------------------\nrandom string result1: {}--------------------\n",
726 result
727 );
728
729 let length = 38;
730 let result = generate_random_string(length);
731
732 assert!(result.is_some());
733
734 let result = result.unwrap();
735
736 assert_eq!(length, result.len() as u32);
737
738 println!(
739 "--------------------------\nrandom string result2: {}\n--------------------\n",
740 result
741 );
742
743 loop {
744 let result: Option<String> = generate_random_string_with_spec(40);
745
746 if result.is_none() {
747 continue;
748 }
749
750 if result.as_ref().unwrap().contains("!") {
751 break;
752 }
753 }
754 }
755}