Skip to main content

lcc/
lib.rs

1use regex::Regex;
2use once_cell::sync::Lazy;
3
4/// PART.1 定義共同特徵 (trait)
5/// 共同行為或功能: 回傳對應字串 (value) 與 標籤 (label)
6pub trait Validated {
7    fn value(&self) -> &str;
8    fn label(&self) -> &str;    
9}
10
11
12/// PART.2 驗證身分證字號:
13/// 1. 總長度必須為 10
14/// 2. 第 1 碼為英文字母
15/// 3. 第 2~10 碼為數字
16#[derive(Debug)] 
17pub struct TaiwanIdValidation(String);
18
19impl TaiwanIdValidation {
20    pub fn validate_id(id: &str) -> Result<Self, String>{
21    // 移除前後空白與換行符號
22    let id = id.trim();
23
24    // 字串長度驗證
25    if id.len() != 10 {
26        return Err("身分證字號長度必須為 10 碼".to_string());
27    }
28
29    // UTF-8字串轉換成位元組
30    let mut chars = id.chars();
31
32    // 第1碼驗證,使用迭代器與next。
33    match chars.next(){
34        Some(c) if c.is_ascii_alphabetic() => {} //配對守護
35        _ => return Err("身分證字號第1碼必須是英文字母".to_string()),
36    }
37    
38    // 第2至第10碼驗證,接續迭代器。
39    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
54/// PART.3 驗證手機號碼:
55/// 1. 必須以 09 開頭
56/// 2. 將"-"連字號分隔的號碼也納入
57/// 3. 告知正確格式
58pub struct PhoneValidation(String);
59
60impl PhoneValidation{
61    pub fn validate_phone(id: &str) -> Result<Self, String>{
62        let id = id.trim();
63
64        // 編譯 Regex (通用格式: r"^...$")
65        let re = Regex::new(r"^09\d{2}-?\d{3}-?\d{3}$").unwrap();
66        
67        // 字串格式驗證
68        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
82/// PART.4 驗證電子信箱:
83/// 1. 必須包括 "@" 符號與"." 符號
84/// 2. 將大小寫英文與數字都納入
85/// 3. 只從字串編譯一次正則表達式 (once_cell crate)
86pub struct MailValidation(String);
87
88impl MailValidation{
89    pub fn validate_mail(id: &str) -> Result<Self, String>{
90        let id = id.trim();
91
92        // 編譯 lazy_static Regex需使用匹配大寫變數名 
93        static EMAIL_REGEX: Lazy<Regex> = Lazy::new(|| {
94            Regex::new(r"^[a-zA-Z0-9]+@[a-zA-Z0-9]+\.com$").unwrap()
95        });
96        // 字串格式驗證
97        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/// PART.5 邏輯測試
113#[cfg(test)]
114mod tests {
115    use super::*; // 引入外部定義的結構體與 Trait
116
117    // ==========================================
118    // 1. 身分證字號測試
119    // ==========================================
120    #[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    // ==========================================
140    // 2. 手機號碼測試
141    // ==========================================
142    #[test]
143    fn test_phone_formats() {
144        // 測試無連字號
145        assert!(PhoneValidation::validate_phone("0912345678").is_ok());
146        // 測試有連字號
147        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    // ==========================================
157    // 3. 電子信箱測試
158    // ==========================================
159    #[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        // 結尾必須是 .com
173        let result = MailValidation::validate_mail("user@example");
174        assert!(result.is_err());
175    }
176}