1use regex::Regex;
2use once_cell::sync::Lazy;
3
4pub trait Validated {
7 fn value(&self) -> &str;
8 fn label(&self) -> &str;
9}
10
11
12#[derive(Debug)]
17pub struct TaiwanIdValidation(String);
18
19impl TaiwanIdValidation {
20 pub fn validate_id(id: &str) -> Result<Self, String>{
21 let id = id.trim();
23
24 if id.len() != 10 {
26 return Err("身分證字號長度必須為 10 碼".to_string());
27 }
28
29 let mut chars = id.chars();
31
32 match chars.next(){
34 Some(c) if c.is_ascii_alphabetic() => {} _ => return Err("身分證字號第1碼必須是英文字母".to_string()),
36 }
37
38 if !chars.all(|c: char| c.is_ascii_digit()) {
40 return Err("身分證字號第 2~10 碼必須為數字".to_string());
41 }
42
43 Ok(TaiwanIdValidation(id.to_string()))
44 }
45
46}
47
48impl Validated for TaiwanIdValidation {
49 fn value(&self) -> &str { &self.0 }
50 fn label(&self) -> &str {"身分證字號"}
51}
52
53
54pub struct PhoneValidation(String);
59
60impl PhoneValidation{
61 pub fn validate_phone(id: &str) -> Result<Self, String>{
62 let id = id.trim();
63
64 let re = Regex::new(r"^09\d{2}-?\d{3}-?\d{3}$").unwrap();
66
67 if re.is_match(id) {
69 Ok(PhoneValidation(id.to_string()))
70 } else {
71 Err("手機號碼格式錯誤,請輸入 09xxxxxxxx 或 09xx-xxx-xxx".to_string())
72 }
73 }
74
75}
76
77impl Validated for PhoneValidation {
78 fn value(&self) -> &str { &self.0 }
79 fn label(&self) -> &str {"手機號碼"}
80}
81
82pub struct MailValidation(String);
87
88impl MailValidation{
89 pub fn validate_mail(id: &str) -> Result<Self, String>{
90 let id = id.trim();
91
92 static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
94 Regex::new(r"^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.com$").unwrap()
95 });
96 if EMAIL_REGEX.is_match(id) {
98 Ok(MailValidation(id.to_string()))
99 } else {
100 Err("信箱格式錯誤,請輸入 xxx@example.com".to_string())
101 }
102 }
103
104}
105
106impl Validated for MailValidation {
107 fn value(&self) -> &str { &self.0 }
108 fn label(&self) -> &str {"電子信箱"}
109}
110
111
112#[cfg(test)]
114mod tests {
115 use super::*; #[test]
121 fn test_id_valid() {
122 assert!(TaiwanIdValidation::validate_id("A123456789").is_ok());
123 }
124
125 #[test]
126 fn test_id_wrong_length() {
127 let result = TaiwanIdValidation::validate_id("A123");
128 assert!(result.is_err());
129 assert_eq!(result.unwrap_err(), "身分證字號長度必須為 10 碼");
130 }
131
132 #[test]
133 fn test_id_wrong_first_char() {
134 let result = TaiwanIdValidation::validate_id("1234567890");
135 assert!(result.is_err());
136 assert_eq!(result.unwrap_err(), "身分證字號第1碼必須是英文字母");
137 }
138
139 #[test]
143 fn test_phone_formats() {
144 assert!(PhoneValidation::validate_phone("0912345678").is_ok());
146 assert!(PhoneValidation::validate_phone("0912-345-678").is_ok());
148 }
149
150 #[test]
151 fn test_phone_invalid_start() {
152 let result = PhoneValidation::validate_phone("0800123456");
153 assert!(result.is_err());
154 }
155
156 #[test]
160 fn test_mail_valid() {
161 assert!(MailValidation::validate_mail("user@example.com").is_ok());
162 }
163
164 #[test]
165 fn test_mail_missing_at() {
166 let result = MailValidation::validate_mail("userexample.com");
167 assert!(result.is_err());
168 }
169
170 #[test]
171 fn test_mail_no_dot_com() {
172 let result = MailValidation::validate_mail("user@example");
174 assert!(result.is_err());
175 }
176}