domain/rdata/
aaaa.rs

1//! Record data from [RFC 3596]: AAAA records.
2//!
3//! This RFC defines the Aaaa record type.
4//!
5//! [RFC 3596]: https://tools.ietf.org/html/rfc3596
6
7use crate::base::cmp::CanonicalOrd;
8use crate::base::iana::Rtype;
9use crate::base::net::Ipv6Addr;
10use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
11use crate::base::scan::{Scanner, ScannerError};
12use crate::base::wire::{Composer, Parse, ParseError};
13use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
14use core::cmp::Ordering;
15use core::convert::Infallible;
16use core::str::FromStr;
17use core::{fmt, str};
18use octseq::octets::OctetsFrom;
19use octseq::parse::Parser;
20
21//------------ Aaaa ---------------------------------------------------------
22
23#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25pub struct Aaaa {
26    addr: Ipv6Addr,
27}
28
29impl Aaaa {
30    /// The rtype of this record data type.
31    pub(crate) const RTYPE: Rtype = Rtype::AAAA;
32}
33
34impl Aaaa {
35    #[must_use]
36    pub fn new(addr: Ipv6Addr) -> Aaaa {
37        Aaaa { addr }
38    }
39
40    #[must_use]
41    pub fn addr(&self) -> Ipv6Addr {
42        self.addr
43    }
44    pub fn set_addr(&mut self, addr: Ipv6Addr) {
45        self.addr = addr
46    }
47
48    pub(super) fn convert_octets<E>(self) -> Result<Self, E> {
49        Ok(self)
50    }
51
52    pub(super) fn flatten<E>(self) -> Result<Self, E> {
53        Ok(self)
54    }
55
56    pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
57        parser: &mut Parser<Octs>,
58    ) -> Result<Self, ParseError> {
59        Ipv6Addr::parse(parser).map(Self::new)
60    }
61
62    pub fn scan<S: Scanner>(scanner: &mut S) -> Result<Self, S::Error> {
63        let token = scanner.scan_octets()?;
64        let token = str::from_utf8(token.as_ref())
65            .map_err(|_| S::Error::custom("expected IPv6 address"))?;
66        Aaaa::from_str(token)
67            .map_err(|_| S::Error::custom("expected IPv6 address"))
68    }
69}
70
71//--- From and FromStr
72
73impl From<Ipv6Addr> for Aaaa {
74    fn from(addr: Ipv6Addr) -> Self {
75        Self::new(addr)
76    }
77}
78
79impl From<Aaaa> for Ipv6Addr {
80    fn from(data: Aaaa) -> Self {
81        data.addr
82    }
83}
84
85impl FromStr for Aaaa {
86    type Err = <Ipv6Addr as core::str::FromStr>::Err;
87
88    fn from_str(s: &str) -> Result<Self, Self::Err> {
89        Ipv6Addr::from_str(s).map(Aaaa::new)
90    }
91}
92
93//--- OctetsFrom
94
95impl OctetsFrom<Aaaa> for Aaaa {
96    type Error = Infallible;
97
98    fn try_octets_from(source: Aaaa) -> Result<Self, Self::Error> {
99        Ok(source)
100    }
101}
102
103//--- CanonicalOrd
104
105impl CanonicalOrd for Aaaa {
106    fn canonical_cmp(&self, other: &Self) -> Ordering {
107        self.cmp(other)
108    }
109}
110
111//--- RecordData, ParseRecordData, ComposeRecordData
112
113impl RecordData for Aaaa {
114    fn rtype(&self) -> Rtype {
115        Self::RTYPE
116    }
117}
118
119impl<'a, Octs: AsRef<[u8]> + ?Sized> ParseRecordData<'a, Octs> for Aaaa {
120    fn parse_rdata(
121        rtype: Rtype,
122        parser: &mut Parser<'a, Octs>,
123    ) -> Result<Option<Self>, ParseError> {
124        if rtype == Self::RTYPE {
125            Self::parse(parser).map(Some)
126        } else {
127            Ok(None)
128        }
129    }
130}
131
132impl ComposeRecordData for Aaaa {
133    fn rdlen(&self, _compress: bool) -> Option<u16> {
134        Some(16)
135    }
136
137    fn compose_rdata<Target: Composer + ?Sized>(
138        &self,
139        target: &mut Target,
140    ) -> Result<(), Target::AppendError> {
141        target.append_slice(&self.addr().octets())
142    }
143
144    fn compose_canonical_rdata<Target: Composer + ?Sized>(
145        &self,
146        target: &mut Target,
147    ) -> Result<(), Target::AppendError> {
148        self.compose_rdata(target)
149    }
150}
151
152//--- Display
153
154impl fmt::Display for Aaaa {
155    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156        self.addr.fmt(f)
157    }
158}
159
160//--- ZonefileFmt
161
162impl ZonefileFmt for Aaaa {
163    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
164        p.write_token(self.addr)
165    }
166}
167
168//--- AsRef and AsMut
169
170impl AsRef<Ipv6Addr> for Aaaa {
171    fn as_ref(&self) -> &Ipv6Addr {
172        &self.addr
173    }
174}
175
176impl AsMut<Ipv6Addr> for Aaaa {
177    fn as_mut(&mut self) -> &mut Ipv6Addr {
178        &mut self.addr
179    }
180}
181
182//============ Testing ======================================================
183
184#[cfg(test)]
185#[cfg(all(feature = "std", feature = "bytes"))]
186mod test {
187    use super::*;
188    use crate::base::rdata::test::{
189        test_compose_parse, test_rdlen, test_scan,
190    };
191
192    #[test]
193    fn aaaa_compose_parse_scan() {
194        let addr = "2001:db9::12:13";
195        let rdata = Aaaa::from_str(addr).unwrap();
196        test_rdlen(&rdata);
197        test_compose_parse(&rdata, Aaaa::parse);
198        test_scan(&[addr], Aaaa::scan, &rdata);
199    }
200}