1use crate::error::Result;
4use crate::matcher;
5use core::{fmt, str};
6use psl_types::{List, Suffix, Type};
7
8#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
10pub struct Name<'a> {
11 full: &'a str,
12 suffix: Option<Suffix<'a>>,
13}
14
15impl<'a> Name<'a> {
16 pub(crate) fn parse<T: List + ?Sized>(list: &T, name: &'a str) -> Result<Name<'a>> {
17 matcher::is_dns_name(name)?;
18 Ok(Self {
19 full: name,
20 suffix: list.suffix(name.as_bytes()),
21 })
22 }
23
24 pub const fn as_str(&self) -> &'a str {
26 self.full
27 }
28
29 fn without_suffix(&self) -> Option<&'a str> {
30 let domain_len = self.full.len();
31 let suffix_len = self.suffix()?.len();
32 if domain_len == suffix_len {
33 return None;
34 }
35 self.full.get(..domain_len - suffix_len - 1)
36 }
37
38 pub fn root(&self) -> Option<&'a str> {
40 let offset = self
41 .without_suffix()?
42 .rfind('.')
43 .map(|x| x + 1)
44 .unwrap_or_default();
45 self.full.get(offset..)
46 }
47
48 pub fn prefix(&self) -> Option<&'a str> {
50 let domain_len = self.full.len();
51 let root_len = self.root()?.len();
52 if domain_len == root_len {
53 return None;
54 }
55 self.full.get(..domain_len - root_len - 1)
56 }
57
58 pub fn suffix(&self) -> Option<&'a str> {
60 let bytes = self.suffix.as_ref()?.as_bytes();
61 str::from_utf8(bytes).ok()
62 }
63
64 pub fn has_known_suffix(&self) -> bool {
66 if let Some(suffix) = self.suffix {
67 suffix.is_known()
68 } else {
69 false
70 }
71 }
72
73 pub fn is_icann(&self) -> bool {
78 if let Some(suffix) = self.suffix {
79 suffix.typ() == Some(Type::Icann)
80 } else {
81 false
82 }
83 }
84
85 pub fn is_private(&self) -> bool {
90 if let Some(suffix) = self.suffix {
91 suffix.typ() == Some(Type::Private)
92 } else {
93 false
94 }
95 }
96}
97
98impl fmt::Display for Name<'_> {
99 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100 write!(f, "{}", self.full)
101 }
102}
103
104impl PartialEq<&str> for Name<'_> {
105 fn eq(&self, other: &&str) -> bool {
106 self.full == *other
107 }
108}