1use regex::Regex;
2
3pub struct Domain {
4 domain_str: String,
5}
6
7impl Domain {
8 pub fn new(domain_str: String) -> Self {
9 Self { domain_str }
10 }
11
12 pub fn from_strict_str(domain_str: String) -> Self {
13 Self::new(domain_str)
14 }
15
16 pub fn is_valid(&self) -> bool {
17 Self::is_valid_domain(&self.domain_str)
18 }
19
20 pub fn is_valid_domain(domain_str: &str) -> bool {
21 let domain_str = domain_str.trim();
22 if domain_str.len() < 4 {
23 return false;
24 }
25 if domain_str.starts_with('.') {
26 return false;
27 }
28 if domain_str.ends_with('.') {
29 return false;
30 }
31 if !domain_str.contains('.') {
32 return false;
33 }
34 if domain_str.contains("..") {
35 return false;
36 }
37 let domain_parts: Vec<&str> = domain_str.split('.').collect();
38 if domain_parts.len() < 2 {
39 return false;
40 }
41 if domain_parts.len() > 10 {
42 return false;
43 }
44 if domain_parts.iter().any(|&part| part.len() > 63) {
45 return false;
46 }
47 let re = Regex::new(r"^[a-z0-9]+$").unwrap();
48 if domain_parts.iter().any(|&part| !re.is_match(part)) {
49 return false;
50 }
51
52 if domain_parts.iter().any(|&part| part.starts_with('-')) {
53 return false;
54 }
55 if domain_parts.iter().any(|&part| part.ends_with('-')) {
56 return false;
57 }
58 if domain_parts.iter().any(|&part| part.contains("--")) {
59 return false;
60 }
61 true
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_is_valid_domain() {
71 assert!(Domain::from_strict_str("earthbucks.com".to_string()).is_valid());
72 assert!(!Domain::from_strict_str("earthbucks.com.".to_string()).is_valid());
73 assert!(!Domain::from_strict_str(".earthbucks.com".to_string()).is_valid());
74 assert!(
75 Domain::from_strict_str("node.node.node.node.earthbucks.com".to_string()).is_valid()
76 );
77 assert!(!Domain::from_strict_str(
78 "node.node.node.node.node.node.node.node.node.earthbucks.com".to_string()
79 )
80 .is_valid());
81 }
82}