Skip to main content

mdns_proto/wire/record/
txt.rs

1//! TXT record (key=value text segments, RFC 1035 §3.3.14 + RFC 6763 §6).
2
3use crate::error::{BufferTooShortDetail, ParseError};
4
5/// Parsed TXT record rdata. Provides iteration over `(key, value)` segments
6/// without copying.
7#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
8pub struct Txt<'a> {
9  rdata: &'a [u8],
10}
11
12impl<'a> Txt<'a> {
13  /// Wraps a slice of TXT rdata.
14  pub const fn from_rdata(rdata: &'a [u8]) -> Self {
15    Self { rdata }
16  }
17
18  /// Iterates over the length-prefixed segments of this TXT record, yielding
19  /// each as a raw `&[u8]` (key=value or boolean key per RFC 6763 §6.4).
20  pub fn segments(&self) -> TxtSegments<'a> {
21    TxtSegments { rest: self.rdata }
22  }
23}
24
25/// Iterator over TXT segments.
26pub struct TxtSegments<'a> {
27  rest: &'a [u8],
28}
29
30impl<'a> Iterator for TxtSegments<'a> {
31  type Item = Result<&'a [u8], ParseError>;
32
33  fn next(&mut self) -> Option<Self::Item> {
34    let &len_byte = self.rest.first()?;
35    let len = len_byte as usize;
36    let after_len = match self.rest.get(1..) {
37      Some(s) => s,
38      None => {
39        self.rest = &[];
40        return Some(Err(ParseError::BufferTooShort(BufferTooShortDetail::new(
41          1, 0, 0,
42        ))));
43      }
44    };
45    let (segment, rest) = match after_len.split_at_checked(len) {
46      Some(pair) => pair,
47      None => {
48        self.rest = &[];
49        return Some(Err(ParseError::BufferTooShort(BufferTooShortDetail::new(
50          len,
51          0,
52          after_len.len(),
53        ))));
54      }
55    };
56    self.rest = rest;
57    Some(Ok(segment))
58  }
59}
60
61#[cfg(test)]
62#[allow(clippy::unwrap_used)]
63mod tests;