this repo has no description
1pub const MAX_EMAIL_LENGTH: usize = 254;
2pub const MAX_LOCAL_PART_LENGTH: usize = 64;
3pub const MAX_DOMAIN_LENGTH: usize = 253;
4pub const MAX_DOMAIN_LABEL_LENGTH: usize = 63;
5const EMAIL_LOCAL_SPECIAL_CHARS: &str = ".!#$%&'*+/=?^_`{|}~-";
6
7pub const MIN_HANDLE_LENGTH: usize = 3;
8pub const MAX_HANDLE_LENGTH: usize = 253;
9
10#[derive(Debug, PartialEq)]
11pub enum HandleValidationError {
12 Empty,
13 TooShort,
14 TooLong,
15 InvalidCharacters,
16 StartsWithInvalidChar,
17 EndsWithInvalidChar,
18 ContainsSpaces,
19}
20
21impl std::fmt::Display for HandleValidationError {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 Self::Empty => write!(f, "Handle cannot be empty"),
25 Self::TooShort => write!(f, "Handle must be at least {} characters", MIN_HANDLE_LENGTH),
26 Self::TooLong => write!(f, "Handle exceeds maximum length of {} characters", MAX_HANDLE_LENGTH),
27 Self::InvalidCharacters => write!(f, "Handle contains invalid characters. Only alphanumeric, hyphens, and underscores are allowed"),
28 Self::StartsWithInvalidChar => write!(f, "Handle cannot start with a hyphen or underscore"),
29 Self::EndsWithInvalidChar => write!(f, "Handle cannot end with a hyphen or underscore"),
30 Self::ContainsSpaces => write!(f, "Handle cannot contain spaces"),
31 }
32 }
33}
34
35pub fn validate_short_handle(handle: &str) -> Result<String, HandleValidationError> {
36 let handle = handle.trim();
37
38 if handle.is_empty() {
39 return Err(HandleValidationError::Empty);
40 }
41
42 if handle.contains(' ') || handle.contains('\t') || handle.contains('\n') {
43 return Err(HandleValidationError::ContainsSpaces);
44 }
45
46 if handle.len() < MIN_HANDLE_LENGTH {
47 return Err(HandleValidationError::TooShort);
48 }
49
50 if handle.len() > MAX_HANDLE_LENGTH {
51 return Err(HandleValidationError::TooLong);
52 }
53
54 let first_char = handle.chars().next().unwrap();
55 if first_char == '-' || first_char == '_' {
56 return Err(HandleValidationError::StartsWithInvalidChar);
57 }
58
59 let last_char = handle.chars().last().unwrap();
60 if last_char == '-' || last_char == '_' {
61 return Err(HandleValidationError::EndsWithInvalidChar);
62 }
63
64 for c in handle.chars() {
65 if !c.is_ascii_alphanumeric() && c != '-' && c != '_' {
66 return Err(HandleValidationError::InvalidCharacters);
67 }
68 }
69
70 Ok(handle.to_lowercase())
71}
72
73pub fn is_valid_email(email: &str) -> bool {
74 let email = email.trim();
75 if email.is_empty() || email.len() > MAX_EMAIL_LENGTH {
76 return false;
77 }
78 let parts: Vec<&str> = email.rsplitn(2, '@').collect();
79 if parts.len() != 2 {
80 return false;
81 }
82 let domain = parts[0];
83 let local = parts[1];
84 if local.is_empty() || local.len() > MAX_LOCAL_PART_LENGTH {
85 return false;
86 }
87 if local.starts_with('.') || local.ends_with('.') {
88 return false;
89 }
90 if local.contains("..") {
91 return false;
92 }
93 for c in local.chars() {
94 if !c.is_ascii_alphanumeric() && !EMAIL_LOCAL_SPECIAL_CHARS.contains(c) {
95 return false;
96 }
97 }
98 if domain.is_empty() || domain.len() > MAX_DOMAIN_LENGTH {
99 return false;
100 }
101 if !domain.contains('.') {
102 return false;
103 }
104 for label in domain.split('.') {
105 if label.is_empty() || label.len() > MAX_DOMAIN_LABEL_LENGTH {
106 return false;
107 }
108 if label.starts_with('-') || label.ends_with('-') {
109 return false;
110 }
111 for c in label.chars() {
112 if !c.is_ascii_alphanumeric() && c != '-' {
113 return false;
114 }
115 }
116 }
117 true
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 #[test]
125 fn test_valid_handles() {
126 assert_eq!(validate_short_handle("alice"), Ok("alice".to_string()));
127 assert_eq!(validate_short_handle("bob123"), Ok("bob123".to_string()));
128 assert_eq!(validate_short_handle("user-name"), Ok("user-name".to_string()));
129 assert_eq!(validate_short_handle("user_name"), Ok("user_name".to_string()));
130 assert_eq!(validate_short_handle("UPPERCASE"), Ok("uppercase".to_string()));
131 assert_eq!(validate_short_handle("MixedCase123"), Ok("mixedcase123".to_string()));
132 assert_eq!(validate_short_handle("abc"), Ok("abc".to_string()));
133 }
134
135 #[test]
136 fn test_invalid_handles() {
137 assert_eq!(validate_short_handle(""), Err(HandleValidationError::Empty));
138 assert_eq!(validate_short_handle(" "), Err(HandleValidationError::Empty));
139 assert_eq!(validate_short_handle("ab"), Err(HandleValidationError::TooShort));
140 assert_eq!(validate_short_handle("a"), Err(HandleValidationError::TooShort));
141 assert_eq!(validate_short_handle("test spaces"), Err(HandleValidationError::ContainsSpaces));
142 assert_eq!(validate_short_handle("test\ttab"), Err(HandleValidationError::ContainsSpaces));
143 assert_eq!(validate_short_handle("-starts"), Err(HandleValidationError::StartsWithInvalidChar));
144 assert_eq!(validate_short_handle("_starts"), Err(HandleValidationError::StartsWithInvalidChar));
145 assert_eq!(validate_short_handle("ends-"), Err(HandleValidationError::EndsWithInvalidChar));
146 assert_eq!(validate_short_handle("ends_"), Err(HandleValidationError::EndsWithInvalidChar));
147 assert_eq!(validate_short_handle("test@user"), Err(HandleValidationError::InvalidCharacters));
148 assert_eq!(validate_short_handle("test!user"), Err(HandleValidationError::InvalidCharacters));
149 assert_eq!(validate_short_handle("test.user"), Err(HandleValidationError::InvalidCharacters));
150 }
151
152 #[test]
153 fn test_handle_trimming() {
154 assert_eq!(validate_short_handle(" alice "), Ok("alice".to_string()));
155 }
156
157 #[test]
158 fn test_valid_emails() {
159 assert!(is_valid_email("user@example.com"));
160 assert!(is_valid_email("user.name@example.com"));
161 assert!(is_valid_email("user+tag@example.com"));
162 assert!(is_valid_email("user@sub.example.com"));
163 assert!(is_valid_email("USER@EXAMPLE.COM"));
164 assert!(is_valid_email("user123@example123.com"));
165 assert!(is_valid_email("a@b.co"));
166 }
167 #[test]
168 fn test_invalid_emails() {
169 assert!(!is_valid_email(""));
170 assert!(!is_valid_email("user"));
171 assert!(!is_valid_email("user@"));
172 assert!(!is_valid_email("@example.com"));
173 assert!(!is_valid_email("user@example"));
174 assert!(!is_valid_email("user@@example.com"));
175 assert!(!is_valid_email("user@.example.com"));
176 assert!(!is_valid_email("user@example..com"));
177 assert!(!is_valid_email(".user@example.com"));
178 assert!(!is_valid_email("user.@example.com"));
179 assert!(!is_valid_email("user..name@example.com"));
180 assert!(!is_valid_email("user@-example.com"));
181 assert!(!is_valid_email("user@example-.com"));
182 }
183 #[test]
184 fn test_trimmed_whitespace() {
185 assert!(is_valid_email(" user@example.com "));
186 }
187}