domain/rdata/rfc1035/
a.rs1use crate::base::cmp::CanonicalOrd;
6use crate::base::iana::Rtype;
7use crate::base::net::Ipv4Addr;
8use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
9use crate::base::scan::{Scanner, ScannerError};
10use crate::base::wire::{Composer, Parse, ParseError};
11use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
12use core::cmp::Ordering;
13use core::convert::Infallible;
14use core::str::FromStr;
15use core::{fmt, str};
16use octseq::octets::OctetsFrom;
17use octseq::parse::Parser;
18
19#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct A {
33 addr: Ipv4Addr,
34}
35
36impl A {
37 pub(crate) const RTYPE: Rtype = Rtype::A;
39}
40
41impl A {
42 #[must_use]
44 pub fn new(addr: Ipv4Addr) -> A {
45 A { addr }
46 }
47
48 #[must_use]
50 pub fn from_octets(a: u8, b: u8, c: u8, d: u8) -> A {
51 A::new(Ipv4Addr::new(a, b, c, d))
52 }
53
54 #[must_use]
55 pub fn addr(&self) -> Ipv4Addr {
56 self.addr
57 }
58 pub fn set_addr(&mut self, addr: Ipv4Addr) {
59 self.addr = addr
60 }
61
62 pub(in crate::rdata) fn convert_octets<E>(self) -> Result<Self, E> {
63 Ok(self)
64 }
65
66 pub(in crate::rdata) fn flatten<E>(self) -> Result<Self, E> {
67 Ok(self)
68 }
69
70 pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
71 parser: &mut Parser<Octs>,
72 ) -> Result<Self, ParseError> {
73 Ipv4Addr::parse(parser).map(Self::new)
74 }
75
76 pub fn scan<S: Scanner>(scanner: &mut S) -> Result<Self, S::Error> {
77 let token = scanner.scan_octets()?;
78 let token = str::from_utf8(token.as_ref())
79 .map_err(|_| S::Error::custom("expected IPv4 address"))?;
80 A::from_str(token)
81 .map_err(|_| S::Error::custom("expected IPv4 address"))
82 }
83}
84
85impl OctetsFrom<A> for A {
88 type Error = Infallible;
89
90 fn try_octets_from(source: A) -> Result<Self, Self::Error> {
91 Ok(source)
92 }
93}
94
95impl From<Ipv4Addr> for A {
98 fn from(addr: Ipv4Addr) -> Self {
99 Self::new(addr)
100 }
101}
102
103impl From<A> for Ipv4Addr {
104 fn from(a: A) -> Self {
105 a.addr
106 }
107}
108
109impl FromStr for A {
110 type Err = <Ipv4Addr as FromStr>::Err;
111
112 fn from_str(s: &str) -> Result<Self, Self::Err> {
113 Ipv4Addr::from_str(s).map(A::new)
114 }
115}
116
117impl CanonicalOrd for A {
120 fn canonical_cmp(&self, other: &Self) -> Ordering {
121 self.cmp(other)
122 }
123}
124
125impl RecordData for A {
128 fn rtype(&self) -> Rtype {
129 A::RTYPE
130 }
131}
132
133impl<'a, Octs: AsRef<[u8]> + ?Sized> ParseRecordData<'a, Octs> for A {
134 fn parse_rdata(
135 rtype: Rtype,
136 parser: &mut Parser<'a, Octs>,
137 ) -> Result<Option<Self>, ParseError> {
138 if rtype == A::RTYPE {
139 Self::parse(parser).map(Some)
140 } else {
141 Ok(None)
142 }
143 }
144}
145
146impl ComposeRecordData for A {
147 fn rdlen(&self, _compress: bool) -> Option<u16> {
148 Some(4)
149 }
150
151 fn compose_rdata<Target: Composer + ?Sized>(
152 &self,
153 target: &mut Target,
154 ) -> Result<(), Target::AppendError> {
155 target.append_slice(&self.addr.octets())
156 }
157
158 fn compose_canonical_rdata<Target: Composer + ?Sized>(
159 &self,
160 target: &mut Target,
161 ) -> Result<(), Target::AppendError> {
162 self.compose_rdata(target)
163 }
164}
165
166impl fmt::Display for A {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 self.addr.fmt(f)
171 }
172}
173
174impl ZonefileFmt for A {
177 fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
178 p.write_token(self.addr)
179 }
180}
181
182impl AsRef<Ipv4Addr> for A {
185 fn as_ref(&self) -> &Ipv4Addr {
186 &self.addr
187 }
188}
189
190impl AsMut<Ipv4Addr> for A {
191 fn as_mut(&mut self) -> &mut Ipv4Addr {
192 &mut self.addr
193 }
194}
195
196#[cfg(test)]
199#[cfg(all(feature = "std", feature = "bytes"))]
200mod test {
201 use super::*;
202 use crate::base::rdata::test::{
203 test_compose_parse, test_rdlen, test_scan,
204 };
205
206 #[test]
207 fn a_compose_parse_scan() {
208 let rdata = A::from_octets(1, 2, 3, 4);
209 test_rdlen(&rdata);
210 test_compose_parse(&rdata, A::parse);
211 test_scan(&["1.2.3.4"], A::scan, &rdata);
212 }
213}