1use std::borrow::Cow;
2
3pub fn normalize_domain(s: &str) -> Cow<'_, str> {
8 let trimmed = s.strip_suffix('.').unwrap_or(s);
9 if trimmed.bytes().any(|b| b.is_ascii_uppercase()) {
10 Cow::Owned(trimmed.to_ascii_lowercase())
11 } else {
12 Cow::Borrowed(trimmed)
13 }
14}
15
16pub fn domain_str(s: &str) -> Option<&str> {
20 psl::domain_str(s)
21}
22
23pub fn suffix_str(s: &str) -> Option<&str> {
27 psl::suffix_str(s)
28}
29
30pub fn suffix_is_known(s: &str) -> bool {
36 psl::suffix(s.as_bytes())
37 .map(|suffix| suffix.is_known())
38 .unwrap_or(false)
39}
40
41pub fn domain_is_known(s: &str) -> bool {
46 psl::domain(s.as_bytes())
47 .map(|domain| domain.suffix().is_known())
48 .unwrap_or(false)
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54
55 #[test]
56 fn already_normalized_borrows() {
57 let s = "example.com";
58 let n = normalize_domain(s);
59 assert!(matches!(n, Cow::Borrowed(_)));
60 assert_eq!(n, "example.com");
61 }
62
63 #[test]
64 fn trims_trailing_dot_without_alloc() {
65 let n = normalize_domain("example.com.");
66 assert!(matches!(n, Cow::Borrowed(_)));
67 assert_eq!(n, "example.com");
68 }
69
70 #[test]
71 fn lowercases_when_needed() {
72 let n = normalize_domain("Example.COM");
73 assert!(matches!(n, Cow::Owned(_)));
74 assert_eq!(n, "example.com");
75 }
76
77 #[test]
78 fn trailing_dot_and_uppercase() {
79 let n = normalize_domain("Example.COM.");
80 assert_eq!(n, "example.com");
81 }
82
83 #[test]
84 fn empty() {
85 assert_eq!(normalize_domain(""), "");
86 }
87
88 #[test]
89 fn domain_str_after_normalize() {
90 let n = normalize_domain("foo.Example.COM.");
91 assert_eq!(domain_str(&n), Some("example.com"));
92 }
93
94 #[test]
95 fn suffix_is_known_known() {
96 let n = normalize_domain("www.example.com");
97 assert_eq!(suffix_is_known(&n), true);
98 }
99
100 #[test]
101 fn suffix_is_known_unknown_fallback() {
102 let n = normalize_domain("domain.invalid");
103 assert_eq!(suffix_is_known(&n), false);
104 }
105
106 #[test]
107 fn suffix_is_known_empty() {
108 let n = normalize_domain("");
109 assert_eq!(suffix_is_known(&n), false);
110 }
111
112 #[test]
113 fn domain_is_known_known() {
114 let n = normalize_domain("www.example.com");
115 assert_eq!(domain_is_known(&n), true);
116 }
117
118 #[test]
119 fn domain_is_known_unknown_fallback() {
120 let n = normalize_domain("domain.invalid");
121 assert_eq!(domain_is_known(&n), false);
122 }
123
124 #[test]
125 fn domain_is_known_empty() {
126 let n = normalize_domain("");
127 assert_eq!(domain_is_known(&n), false);
128 }
129}