Coverage Report

Created: 2025-02-21 07:11

/rust/registry/src/index.crates.io-6f17d22bba15001f/addr-0.15.6/src/domain.rs
Line
Count
Source (jump to first uncovered line)
1
//! Domain name types
2
3
use crate::error::{Kind, Result};
4
use crate::matcher;
5
use core::fmt;
6
use psl_types::{List, Type};
7
8
/// Holds information about a particular domain name
9
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
10
pub struct Name<'a> {
11
    full: &'a str,
12
    suffix: psl_types::Suffix<'a>,
13
}
14
15
impl<'a> Name<'a> {
16
0
    pub(crate) fn parse<T: List + ?Sized>(list: &T, name: &'a str) -> Result<Name<'a>> {
17
0
        let stripped = if name.ends_with('.') {
18
0
            name.get(..name.len() - 1).unwrap_or_default()
19
        } else {
20
0
            name
21
        };
22
0
        if stripped.contains('.') {
23
0
            matcher::is_domain_name(stripped)?;
24
        } else {
25
0
            matcher::is_label(stripped, true)?;
26
        }
27
        Ok(Self {
28
0
            suffix: list.suffix(name.as_bytes()).ok_or(Kind::InvalidDomain)?,
29
0
            full: name,
30
        })
31
0
    }
32
33
    /// Full domain name as a `str`
34
0
    pub const fn as_str(&self) -> &'a str {
35
0
        self.full
36
0
    }
37
38
0
    fn without_suffix(&self) -> Option<&'a str> {
39
0
        let domain_len = self.full.len();
40
0
        let suffix_len = self.suffix().len();
41
0
        if domain_len == suffix_len {
42
0
            return None;
43
0
        }
44
0
        self.full.get(..domain_len - suffix_len - 1)
45
0
    }
46
47
    /// The root domain (the registrable part)
48
0
    pub fn root(&self) -> Option<&'a str> {
49
0
        let offset = self
50
0
            .without_suffix()?
51
0
            .rfind('.')
52
0
            .map(|x| x + 1)
53
0
            .unwrap_or_default();
54
0
        self.full.get(offset..)
55
0
    }
56
57
    /// The part before the root domain (aka. subdomain)
58
0
    pub fn prefix(&self) -> Option<&'a str> {
59
0
        let domain_len = self.full.len();
60
0
        let root_len = self.root()?.len();
61
0
        if domain_len == root_len {
62
0
            return None;
63
0
        }
64
0
        self.full.get(..domain_len - root_len - 1)
65
0
    }
66
67
    /// The domain name suffix (extension)
68
0
    pub fn suffix(&self) -> &'a str {
69
0
        let offset = self.full.len() - self.suffix.as_bytes().len();
70
0
        &self.full[offset..]
71
0
    }
72
73
    /// Whether the suffix of the domain name is in the Public Suffix List
74
0
    pub fn has_known_suffix(&self) -> bool {
75
0
        self.suffix.is_known()
76
0
    }
77
78
    /// Whether this an ICANN delegated suffix
79
    ///
80
    /// ICANN domains are those delegated by ICANN or part of the IANA root
81
    /// zone database
82
0
    pub fn is_icann(&self) -> bool {
83
0
        self.suffix.typ() == Some(Type::Icann)
84
0
    }
85
86
    /// Whether this is a private party delegated suffix
87
    ///
88
    /// PRIVATE domains are amendments submitted by the domain holder, as an
89
    /// expression of how they operate their domain security policy
90
0
    pub fn is_private(&self) -> bool {
91
0
        self.suffix.typ() == Some(Type::Private)
92
0
    }
93
}
94
95
impl fmt::Display for Name<'_> {
96
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97
0
        write!(f, "{}", self.full)
98
0
    }
99
}
100
101
impl PartialEq<&str> for Name<'_> {
102
0
    fn eq(&self, other: &&str) -> bool {
103
0
        self.full == *other
104
0
    }
105
}