hickory_proto/rr/rdata/
txt.rs1use alloc::{
10 boxed::Box,
11 string::{String, ToString},
12 vec::Vec,
13};
14use core::fmt;
15
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18
19use crate::{
20 error::ProtoResult,
21 rr::{RData, RecordData, RecordDataDecodable, RecordType},
22 serialize::{binary::*, txt::ParseError},
23};
24
25#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
39#[derive(Debug, PartialEq, Eq, Hash, Clone)]
40#[non_exhaustive]
41pub struct TXT {
42 pub txt_data: Box<[Box<[u8]>]>,
46}
47
48impl TXT {
49 pub fn new(txt_data: Vec<String>) -> Self {
59 Self {
60 txt_data: txt_data
61 .into_iter()
62 .map(|s| s.into_bytes().into_boxed_slice())
63 .collect::<Vec<_>>()
64 .into_boxed_slice(),
65 }
66 }
67
68 pub fn from_bytes(txt_data: Vec<&[u8]>) -> Self {
79 Self {
80 txt_data: txt_data
81 .into_iter()
82 .map(|s| s.to_vec().into_boxed_slice())
83 .collect::<Vec<_>>()
84 .into_boxed_slice(),
85 }
86 }
87
88 #[allow(clippy::unnecessary_wraps)]
90 pub(crate) fn from_tokens<'i, I: Iterator<Item = &'i str>>(
91 tokens: I,
92 ) -> Result<Self, ParseError> {
93 let txt_data = tokens.map(ToString::to_string).collect::<Vec<_>>();
94 Ok(Self::new(txt_data))
95 }
96}
97
98impl BinEncodable for TXT {
99 fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
100 for s in &self.txt_data {
101 encoder.emit_character_data(s)?;
102 }
103
104 Ok(())
105 }
106}
107
108impl RecordDataDecodable<'_> for TXT {
109 fn read_data(
110 decoder: &mut BinDecoder<'_>,
111 rdata_length: Restrict<u16>,
112 ) -> Result<Self, DecodeError> {
113 let data_len = decoder.len();
114 let mut strings = Vec::with_capacity(1);
115
116 let rdata_length =
118 rdata_length.map(|u| u as usize).unverified();
119 while data_len - decoder.len() < rdata_length {
120 let string = decoder.read_character_data()?.unverified();
121 strings.push(string.to_vec().into_boxed_slice());
122 }
123 Ok(Self {
124 txt_data: strings.into_boxed_slice(),
125 })
126 }
127}
128
129impl RecordData for TXT {
130 fn try_borrow(data: &RData) -> Option<&Self> {
131 match data {
132 RData::TXT(data) => Some(data),
133 _ => None,
134 }
135 }
136
137 fn record_type(&self) -> RecordType {
138 RecordType::TXT
139 }
140
141 fn into_rdata(self) -> RData {
142 RData::TXT(self)
143 }
144}
145
146impl fmt::Display for TXT {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
170 for txt in self.txt_data.iter() {
171 f.write_str(&String::from_utf8_lossy(txt))?;
172 }
173
174 Ok(())
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 #![allow(clippy::dbg_macro, clippy::print_stdout)]
181
182 use alloc::string::ToString;
183 #[cfg(feature = "std")]
184 use std::println;
185
186 use super::*;
187
188 #[test]
189 fn test() {
190 let rdata = TXT::new(vec!["Test me some".to_string(), "more please".to_string()]);
191
192 let mut bytes = Vec::new();
193 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
194 assert!(rdata.emit(&mut encoder).is_ok());
195 let bytes = encoder.into_bytes();
196
197 #[cfg(feature = "std")]
198 println!("bytes: {bytes:?}");
199
200 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
201 let restrict = Restrict::new(bytes.len() as u16);
202 let read_rdata = TXT::read_data(&mut decoder, restrict).expect("Decoding error");
203 assert_eq!(rdata, read_rdata);
204 }
205
206 #[test]
207 fn publish_binary_txt_record() {
208 let bin_data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
209 let rdata = TXT::from_bytes(vec![b"Test me some", &bin_data]);
210
211 let mut bytes = Vec::new();
212 let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
213 assert!(rdata.emit(&mut encoder).is_ok());
214 let bytes = encoder.into_bytes();
215
216 #[cfg(feature = "std")]
217 println!("bytes: {bytes:?}");
218
219 let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
220 let restrict = Restrict::new(bytes.len() as u16);
221 let read_rdata = TXT::read_data(&mut decoder, restrict).expect("Decoding error");
222 assert_eq!(rdata, read_rdata);
223 }
224}