Coverage Report

Created: 2026-06-30 07:02

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