teaql_tool_std/
desensitize.rs1pub struct DesensitizeTool;
2
3impl DesensitizeTool {
4 pub fn new() -> Self {
5 Self
6 }
7
8 pub fn chinese_id_card(&self, id_card: impl AsRef<str>) -> String {
12 let s = id_card.as_ref();
13 if s.len() == 18 {
14 format!("{}********{}", &s[0..6], &s[14..18])
15 } else if s.len() == 15 {
16 format!("{}******{}", &s[0..6], &s[12..15])
17 } else {
18 s.to_string()
19 }
20 }
21
22 pub fn chinese_phone(&self, phone: impl AsRef<str>) -> String {
24 let s = phone.as_ref();
25 if s.len() == 11 {
26 format!("{}****{}", &s[0..3], &s[7..11])
27 } else {
28 s.to_string()
29 }
30 }
31
32 pub fn chinese_name(&self, name: impl AsRef<str>) -> String {
34 let s = name.as_ref();
35 let chars: Vec<char> = s.chars().collect();
36 match chars.len() {
37 0 => String::new(),
38 1 => s.to_string(),
39 2 => format!("{}*", chars[0]),
40 _ => format!("{}*{}", chars[0], chars[chars.len() - 1]),
41 }
42 }
43
44 pub fn us_ssn(&self, ssn: impl AsRef<str>) -> String {
48 let s = ssn.as_ref();
49 if s.len() == 11 && s.chars().nth(3) == Some('-') && s.chars().nth(6) == Some('-') {
50 format!("***-**-{}", &s[7..11])
51 } else if s.len() == 9 {
52 format!("*****{}", &s[5..9])
53 } else {
54 s.to_string()
55 }
56 }
57
58 pub fn us_phone(&self, phone: impl AsRef<str>) -> String {
60 let s = phone.as_ref();
61 let mut result = String::with_capacity(s.len());
63 let digit_count = s.chars().filter(|c| c.is_ascii_digit()).count();
64 let mut digits_seen = 0;
65
66 for c in s.chars() {
67 if c.is_ascii_digit() {
68 digits_seen += 1;
69 if digits_seen <= digit_count.saturating_sub(4) {
70 result.push('*');
71 } else {
72 result.push(c);
73 }
74 } else {
75 result.push(c);
76 }
77 }
78 result
79 }
80
81 pub fn credit_card(&self, card: impl AsRef<str>) -> String {
83 let s = card.as_ref();
84 let mut result = String::with_capacity(s.len());
85 let digit_count = s.chars().filter(|c| c.is_ascii_digit()).count();
86 let mut digits_seen = 0;
87
88 for c in s.chars() {
89 if c.is_ascii_digit() {
90 digits_seen += 1;
91 if digits_seen <= digit_count.saturating_sub(4) {
92 result.push('*');
93 } else {
94 result.push(c);
95 }
96 } else {
97 result.push(c);
98 }
99 }
100 result
101 }
102
103 pub fn email(&self, email: impl AsRef<str>) -> String {
107 let s = email.as_ref();
108 if let Some(idx) = s.find('@') {
109 let prefix = &s[..idx];
110 let domain = &s[idx..];
111 if prefix.len() <= 1 {
112 format!("*{}", domain)
113 } else {
114 format!("{}***{}", &prefix[..1], domain)
115 }
116 } else {
117 s.to_string()
118 }
119 }
120
121 pub fn password(&self, _pwd: impl AsRef<str>) -> String {
123 "******".to_string()
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_desensitize() {
133 let t = DesensitizeTool::new();
134 assert_eq!(t.chinese_phone("13812345678"), "138****5678");
135 assert_eq!(t.chinese_id_card("110105199001011234"), "110105********1234");
136 assert_eq!(t.chinese_name("张三"), "张*");
137 assert_eq!(t.chinese_name("王大拿"), "王*拿");
138 assert_eq!(t.us_ssn("123-45-6789"), "***-**-6789");
139 assert_eq!(t.us_phone("+1-555-123-4567"), "+*-***-***-4567");
140 assert_eq!(t.credit_card("1234-5678-1234-5678"), "****-****-****-5678");
141 assert_eq!(t.email("john.doe@example.com"), "j***@example.com");
142 }
143}