domain/rdata/rfc1035/
null.rs1#![allow(clippy::needless_maybe_sized)]
8
9use crate::base::cmp::CanonicalOrd;
10use crate::base::iana::Rtype;
11use crate::base::rdata::{
12 ComposeRecordData, LongRecordData, ParseRecordData, RecordData,
13};
14use crate::base::wire::{Composer, ParseError};
15use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
16use core::cmp::Ordering;
17use core::{fmt, hash, mem};
18use octseq::octets::{Octets, OctetsFrom, OctetsInto};
19use octseq::parse::Parser;
20
21#[derive(Clone)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[repr(transparent)]
34pub struct Null<Octs: ?Sized> {
35 #[cfg_attr(
36 feature = "serde",
37 serde(
38 serialize_with = "octseq::serde::SerializeOctets::serialize_octets",
39 deserialize_with = "octseq::serde::DeserializeOctets::deserialize_octets",
40 bound(
41 serialize = "Octs: octseq::serde::SerializeOctets",
42 deserialize = "Octs: octseq::serde::DeserializeOctets<'de>",
43 )
44 )
45 )]
46 data: Octs,
47}
48
49impl Null<()> {
50 pub(crate) const RTYPE: Rtype = Rtype::NULL;
52}
53
54impl<Octs> Null<Octs> {
55 pub fn from_octets(data: Octs) -> Result<Self, LongRecordData>
59 where
60 Octs: AsRef<[u8]>,
61 {
62 Null::check_slice(data.as_ref())?;
63 Ok(unsafe { Self::from_octets_unchecked(data) })
64 }
65
66 pub unsafe fn from_octets_unchecked(data: Octs) -> Self {
72 Null { data }
73 }
74}
75
76impl Null<[u8]> {
77 pub fn from_slice(data: &[u8]) -> Result<&Self, LongRecordData> {
81 Self::check_slice(data)?;
82 Ok(unsafe { Self::from_slice_unchecked(data) })
83 }
84
85 #[must_use]
91 pub unsafe fn from_slice_unchecked(data: &[u8]) -> &Self {
92 mem::transmute(data)
94 }
95
96 fn check_slice(slice: &[u8]) -> Result<(), LongRecordData> {
98 LongRecordData::check_len(slice.len())
99 }
100}
101
102impl<Octs: ?Sized> Null<Octs> {
103 pub fn data(&self) -> &Octs {
105 &self.data
106 }
107}
108
109impl<Octs: AsRef<[u8]>> Null<Octs> {
110 pub fn len(&self) -> usize {
111 self.data.as_ref().len()
112 }
113
114 pub fn is_empty(&self) -> bool {
115 self.data.as_ref().is_empty()
116 }
117}
118
119impl<Octs> Null<Octs> {
120 pub(in crate::rdata) fn convert_octets<Target: OctetsFrom<Octs>>(
121 self,
122 ) -> Result<Null<Target>, Target::Error> {
123 Ok(unsafe {
124 Null::from_octets_unchecked(self.data.try_octets_into()?)
125 })
126 }
127
128 pub(in crate::rdata) fn flatten<Target: OctetsFrom<Octs>>(
129 self,
130 ) -> Result<Null<Target>, Target::Error> {
131 self.convert_octets()
132 }
133}
134
135impl<Octs> Null<Octs> {
136 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
137 parser: &mut Parser<'a, Src>,
138 ) -> Result<Self, ParseError> {
139 let len = parser.remaining();
140 parser
141 .parse_octets(len)
142 .map(|res| unsafe { Self::from_octets_unchecked(res) })
143 .map_err(Into::into)
144 }
145}
146
147impl<Octs, SrcOcts> OctetsFrom<Null<SrcOcts>> for Null<Octs>
150where
151 Octs: OctetsFrom<SrcOcts>,
152{
153 type Error = Octs::Error;
154
155 fn try_octets_from(source: Null<SrcOcts>) -> Result<Self, Self::Error> {
156 Octs::try_octets_from(source.data)
157 .map(|res| unsafe { Self::from_octets_unchecked(res) })
158 }
159}
160
161impl<Octs, Other> PartialEq<Null<Other>> for Null<Octs>
164where
165 Octs: AsRef<[u8]> + ?Sized,
166 Other: AsRef<[u8]> + ?Sized,
167{
168 fn eq(&self, other: &Null<Other>) -> bool {
169 self.data.as_ref().eq(other.data.as_ref())
170 }
171}
172
173impl<Octs: AsRef<[u8]> + ?Sized> Eq for Null<Octs> {}
174
175impl<Octs, Other> PartialOrd<Null<Other>> for Null<Octs>
178where
179 Octs: AsRef<[u8]> + ?Sized,
180 Other: AsRef<[u8]> + ?Sized,
181{
182 fn partial_cmp(&self, other: &Null<Other>) -> Option<Ordering> {
183 self.data.as_ref().partial_cmp(other.data.as_ref())
184 }
185}
186
187impl<Octs, Other> CanonicalOrd<Null<Other>> for Null<Octs>
188where
189 Octs: AsRef<[u8]> + ?Sized,
190 Other: AsRef<[u8]> + ?Sized,
191{
192 fn canonical_cmp(&self, other: &Null<Other>) -> Ordering {
193 self.data.as_ref().cmp(other.data.as_ref())
194 }
195}
196
197impl<Octs: AsRef<[u8]> + ?Sized> Ord for Null<Octs> {
198 fn cmp(&self, other: &Self) -> Ordering {
199 self.data.as_ref().cmp(other.data.as_ref())
200 }
201}
202
203impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Null<Octs> {
206 fn hash<H: hash::Hasher>(&self, state: &mut H) {
207 self.data.as_ref().hash(state)
208 }
209}
210
211impl<Octs: ?Sized> RecordData for Null<Octs> {
214 fn rtype(&self) -> Rtype {
215 Null::RTYPE
216 }
217}
218
219impl<'a, Octs> ParseRecordData<'a, Octs> for Null<Octs::Range<'a>>
220where
221 Octs: Octets + ?Sized,
222{
223 fn parse_rdata(
224 rtype: Rtype,
225 parser: &mut Parser<'a, Octs>,
226 ) -> Result<Option<Self>, ParseError> {
227 if rtype == Null::RTYPE {
228 Self::parse(parser).map(Some)
229 } else {
230 Ok(None)
231 }
232 }
233}
234
235impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Null<Octs> {
236 fn rdlen(&self, _compress: bool) -> Option<u16> {
237 Some(
238 u16::try_from(self.data.as_ref().len()).expect("long NULL rdata"),
239 )
240 }
241
242 fn compose_rdata<Target: Composer + ?Sized>(
243 &self,
244 target: &mut Target,
245 ) -> Result<(), Target::AppendError> {
246 target.append_slice(self.data.as_ref())
247 }
248
249 fn compose_canonical_rdata<Target: Composer + ?Sized>(
250 &self,
251 target: &mut Target,
252 ) -> Result<(), Target::AppendError> {
253 self.compose_rdata(target)
254 }
255}
256
257impl<Octs: AsRef<Other>, Other> AsRef<Other> for Null<Octs> {
260 fn as_ref(&self) -> &Other {
261 self.data.as_ref()
262 }
263}
264
265impl<Octs: AsRef<[u8]>> fmt::Display for Null<Octs> {
268 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
269 write!(f, "\\# {}", self.data.as_ref().len())?;
270 for ch in self.data.as_ref().iter() {
271 write!(f, " {:02x}", ch)?;
272 }
273 Ok(())
274 }
275}
276
277impl<Octs: AsRef<[u8]>> fmt::Debug for Null<Octs> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.write_str("Null(")?;
280 fmt::Display::fmt(self, f)?;
281 f.write_str(")")
282 }
283}
284
285impl<Octs: AsRef<[u8]>> ZonefileFmt for Null<Octs> {
288 fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
289 struct Data<'a>(&'a [u8]);
290
291 impl fmt::Display for Data<'_> {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 write!(f, "\\# {}", self.0.len())?;
294 for ch in self.0 {
295 write!(f, " {:02x}", *ch)?
296 }
297 Ok(())
298 }
299 }
300
301 p.write_token(Data(self.data.as_ref()))
302 }
303}
304
305#[cfg(test)]
308#[cfg(all(feature = "std", feature = "bytes"))]
309mod test {
310 use super::*;
311 use crate::base::rdata::test::{test_compose_parse, test_rdlen};
312
313 #[test]
314 #[allow(clippy::redundant_closure)] fn null_compose_parse_scan() {
316 let rdata = Null::from_octets("foo").unwrap();
317 test_rdlen(&rdata);
318 test_compose_parse(&rdata, |parser| Null::parse(parser));
319 }
320}