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