use crate::base::charstr::{CharStr, CharStrError};
use crate::base::cmp::CanonicalOrd;
use crate::base::iana::Rtype;
use crate::base::name::{Dname, ParsedDname, PushError, ToDname};
use crate::base::net::Ipv4Addr;
use crate::base::octets::{
Compose, EmptyBuilder, FromBuilder, OctetsBuilder, OctetsFrom,
OctetsInto, OctetsRef, Parse, ParseError, Parser, ShortBuf,
};
#[cfg(feature = "serde")]
use crate::base::octets::{DeserializeOctets, SerializeOctets};
use crate::base::rdata::RtypeRecordData;
use crate::base::serial::Serial;
use crate::base::str::Symbol;
#[cfg(feature = "master")]
use crate::master::scan::{
CharSource, Scan, ScanError, Scanner, SyntaxError,
};
#[cfg(feature = "master")]
use bytes::Bytes;
#[cfg(feature = "bytes")]
use bytes::BytesMut;
use core::cmp::Ordering;
use core::str::FromStr;
use core::{fmt, hash, ops};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct A {
addr: Ipv4Addr,
}
impl A {
pub fn new(addr: Ipv4Addr) -> A {
A { addr }
}
pub fn from_octets(a: u8, b: u8, c: u8, d: u8) -> A {
A::new(Ipv4Addr::new(a, b, c, d))
}
pub fn addr(&self) -> Ipv4Addr {
self.addr
}
pub fn set_addr(&mut self, addr: Ipv4Addr) {
self.addr = addr
}
pub fn flatten_into(self) -> Result<A, PushError> {
Ok(self)
}
}
impl OctetsFrom<A> for A {
fn octets_from(source: A) -> Result<Self, ShortBuf> {
Ok(source)
}
}
impl From<Ipv4Addr> for A {
fn from(addr: Ipv4Addr) -> Self {
Self::new(addr)
}
}
impl From<A> for Ipv4Addr {
fn from(a: A) -> Self {
a.addr
}
}
#[cfg(feature = "std")]
impl FromStr for A {
type Err = <Ipv4Addr as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ipv4Addr::from_str(s).map(A::new)
}
}
impl CanonicalOrd for A {
fn canonical_cmp(&self, other: &Self) -> Ordering {
self.cmp(other)
}
}
impl<Octets: AsRef<[u8]>> Parse<Octets> for A {
fn parse(parser: &mut Parser<Octets>) -> Result<Self, ParseError> {
Ipv4Addr::parse(parser).map(Self::new)
}
fn skip(parser: &mut Parser<Octets>) -> Result<(), ParseError> {
Ipv4Addr::skip(parser)
}
}
impl Compose for A {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
self.addr.compose(target)
}
}
#[cfg(feature = "master")]
impl Scan for A {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
scanner
.scan_string_phrase(|res| A::from_str(&res).map_err(Into::into))
}
}
impl fmt::Display for A {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.addr.fmt(f)
}
}
impl RtypeRecordData for A {
const RTYPE: Rtype = Rtype::A;
}
impl ops::Deref for A {
type Target = Ipv4Addr;
fn deref(&self) -> &Self::Target {
&self.addr
}
}
impl ops::DerefMut for A {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.addr
}
}
impl AsRef<Ipv4Addr> for A {
fn as_ref(&self) -> &Ipv4Addr {
&self.addr
}
}
impl AsMut<Ipv4Addr> for A {
fn as_mut(&mut self) -> &mut Ipv4Addr {
&mut self.addr
}
}
dname_type! {
(Cname, Cname, cname)
}
#[derive(Clone)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(bound(
serialize = "Octets: AsRef<[u8]> + crate::base::octets::SerializeOctets",
deserialize = "Octets: \
crate::base::octets::FromBuilder \
+ crate::base::octets::DeserializeOctets<'de>, \
<Octets as FromBuilder>::Builder: EmptyBuilder ",
))
)]
pub struct Hinfo<Octets> {
cpu: CharStr<Octets>,
os: CharStr<Octets>,
}
impl<Octets> Hinfo<Octets> {
pub fn new(cpu: CharStr<Octets>, os: CharStr<Octets>) -> Self {
Hinfo { cpu, os }
}
pub fn cpu(&self) -> &CharStr<Octets> {
&self.cpu
}
pub fn os(&self) -> &CharStr<Octets> {
&self.os
}
}
impl<SrcOctets> Hinfo<SrcOctets> {
pub fn flatten_into<Octets>(self) -> Result<Hinfo<Octets>, PushError>
where
Octets: OctetsFrom<SrcOctets>,
{
let Self { cpu, os } = self;
Ok(Hinfo::new(cpu.octets_into()?, os.octets_into()?))
}
}
impl<Octets, SrcOctets> OctetsFrom<Hinfo<SrcOctets>> for Hinfo<Octets>
where
Octets: OctetsFrom<SrcOctets>,
{
fn octets_from(source: Hinfo<SrcOctets>) -> Result<Self, ShortBuf> {
Ok(Hinfo::new(
CharStr::octets_from(source.cpu)?,
CharStr::octets_from(source.os)?,
))
}
}
impl<Octets, Other> PartialEq<Hinfo<Other>> for Hinfo<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn eq(&self, other: &Hinfo<Other>) -> bool {
self.cpu.eq(&other.cpu) && self.os.eq(&other.os)
}
}
impl<Octets: AsRef<[u8]>> Eq for Hinfo<Octets> {}
impl<Octets, Other> PartialOrd<Hinfo<Other>> for Hinfo<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &Hinfo<Other>) -> Option<Ordering> {
match self.cpu.partial_cmp(&other.cpu) {
Some(Ordering::Equal) => {}
other => return other,
}
self.os.partial_cmp(&other.os)
}
}
impl<Octets, Other> CanonicalOrd<Hinfo<Other>> for Hinfo<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn canonical_cmp(&self, other: &Hinfo<Other>) -> Ordering {
match self.cpu.canonical_cmp(&other.cpu) {
Ordering::Equal => {}
other => return other,
}
self.os.canonical_cmp(&other.os)
}
}
impl<Octets: AsRef<[u8]>> Ord for Hinfo<Octets> {
fn cmp(&self, other: &Self) -> Ordering {
match self.cpu.cmp(&other.cpu) {
Ordering::Equal => {}
other => return other,
}
self.os.cmp(&other.os)
}
}
impl<Octets: AsRef<[u8]>> hash::Hash for Hinfo<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.cpu.hash(state);
self.os.hash(state);
}
}
impl<Ref: OctetsRef> Parse<Ref> for Hinfo<Ref::Range> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
Ok(Self::new(CharStr::parse(parser)?, CharStr::parse(parser)?))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
CharStr::skip(parser)?;
CharStr::skip(parser)?;
Ok(())
}
}
impl<Octets: AsRef<[u8]>> Compose for Hinfo<Octets> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
self.cpu.compose(target)?;
self.os.compose(target)
})
}
}
#[cfg(feature = "master")]
impl Scan for Hinfo<Bytes> {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
Ok(Self::new(CharStr::scan(scanner)?, CharStr::scan(scanner)?))
}
}
impl<Octets: AsRef<[u8]>> fmt::Display for Hinfo<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.cpu, self.os)
}
}
impl<Octets: AsRef<[u8]>> fmt::Debug for Hinfo<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Hinfo")
.field("cpu", &self.cpu)
.field("os", &self.os)
.finish()
}
}
impl<Octets> RtypeRecordData for Hinfo<Octets> {
const RTYPE: Rtype = Rtype::Hinfo;
}
dname_type! {
(Mb, Mb, madname)
}
dname_type! {
(Md, Md, madname)
}
dname_type! {
(Mf, Mf, madname)
}
dname_type! {
(Mg, Mg, madname)
}
#[derive(Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Minfo<N> {
rmailbx: N,
emailbx: N,
}
impl<N> Minfo<N> {
pub fn new(rmailbx: N, emailbx: N) -> Self {
Minfo { rmailbx, emailbx }
}
pub fn rmailbx(&self) -> &N {
&self.rmailbx
}
pub fn emailbx(&self) -> &N {
&self.emailbx
}
}
impl<Ref> Minfo<ParsedDname<Ref>>
where
Ref: OctetsRef,
{
pub fn flatten_into<Octets>(
self,
) -> Result<Minfo<Dname<Octets>>, PushError>
where
Octets: OctetsFrom<Ref::Range> + FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
{
let Self { rmailbx, emailbx } = self;
Ok(Minfo::new(rmailbx.flatten_into()?, emailbx.flatten_into()?))
}
}
impl<Name, SrcName> OctetsFrom<Minfo<SrcName>> for Minfo<Name>
where
Name: OctetsFrom<SrcName>,
{
fn octets_from(source: Minfo<SrcName>) -> Result<Self, ShortBuf> {
Ok(Minfo::new(
Name::octets_from(source.rmailbx)?,
Name::octets_from(source.emailbx)?,
))
}
}
impl<N, NN> PartialEq<Minfo<NN>> for Minfo<N>
where
N: ToDname,
NN: ToDname,
{
fn eq(&self, other: &Minfo<NN>) -> bool {
self.rmailbx.name_eq(&other.rmailbx)
&& self.emailbx.name_eq(&other.emailbx)
}
}
impl<N: ToDname> Eq for Minfo<N> {}
impl<N, NN> PartialOrd<Minfo<NN>> for Minfo<N>
where
N: ToDname,
NN: ToDname,
{
fn partial_cmp(&self, other: &Minfo<NN>) -> Option<Ordering> {
match self.rmailbx.name_cmp(&other.rmailbx) {
Ordering::Equal => {}
other => return Some(other),
}
Some(self.emailbx.name_cmp(&other.emailbx))
}
}
impl<N: ToDname> Ord for Minfo<N> {
fn cmp(&self, other: &Self) -> Ordering {
match self.rmailbx.name_cmp(&other.rmailbx) {
Ordering::Equal => {}
other => return other,
}
self.emailbx.name_cmp(&other.emailbx)
}
}
impl<N: ToDname, NN: ToDname> CanonicalOrd<Minfo<NN>> for Minfo<N> {
fn canonical_cmp(&self, other: &Minfo<NN>) -> Ordering {
match self.rmailbx.lowercase_composed_cmp(&other.rmailbx) {
Ordering::Equal => {}
other => return other,
}
self.emailbx.lowercase_composed_cmp(&other.emailbx)
}
}
impl<Ref: OctetsRef> Parse<Ref> for Minfo<ParsedDname<Ref>> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
Ok(Self::new(
ParsedDname::parse(parser)?,
ParsedDname::parse(parser)?,
))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
ParsedDname::skip(parser)?;
ParsedDname::skip(parser)?;
Ok(())
}
}
impl<N: ToDname> Compose for Minfo<N> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
target.append_compressed_dname(&self.rmailbx)?;
target.append_compressed_dname(&self.emailbx)
})
}
fn compose_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
self.rmailbx.compose_canonical(target)?;
self.emailbx.compose_canonical(target)
})
}
}
#[cfg(feature = "master")]
impl<N: Scan> Scan for Minfo<N> {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
Ok(Self::new(N::scan(scanner)?, N::scan(scanner)?))
}
}
impl<N: fmt::Display> fmt::Display for Minfo<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}. {}.", self.rmailbx, self.emailbx)
}
}
impl<N> RtypeRecordData for Minfo<N> {
const RTYPE: Rtype = Rtype::Minfo;
}
dname_type! {
(Mr, Mr, newname)
}
#[derive(Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Mx<N> {
preference: u16,
exchange: N,
}
impl<N> Mx<N> {
pub fn new(preference: u16, exchange: N) -> Self {
Mx {
preference,
exchange,
}
}
pub fn preference(&self) -> u16 {
self.preference
}
pub fn exchange(&self) -> &N {
&self.exchange
}
}
impl<Ref> Mx<ParsedDname<Ref>>
where
Ref: OctetsRef,
{
pub fn flatten_into<Octets>(self) -> Result<Mx<Dname<Octets>>, PushError>
where
Octets: OctetsFrom<<Ref as OctetsRef>::Range> + FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
{
let Self {
preference,
exchange,
} = self;
Ok(Mx::new(preference, exchange.flatten_into()?))
}
}
impl<Name, SrcName> OctetsFrom<Mx<SrcName>> for Mx<Name>
where
Name: OctetsFrom<SrcName>,
{
fn octets_from(source: Mx<SrcName>) -> Result<Self, ShortBuf> {
Ok(Mx::new(
source.preference,
Name::octets_from(source.exchange)?,
))
}
}
impl<N, NN> PartialEq<Mx<NN>> for Mx<N>
where
N: ToDname,
NN: ToDname,
{
fn eq(&self, other: &Mx<NN>) -> bool {
self.preference == other.preference
&& self.exchange.name_eq(&other.exchange)
}
}
impl<N: ToDname> Eq for Mx<N> {}
impl<N, NN> PartialOrd<Mx<NN>> for Mx<N>
where
N: ToDname,
NN: ToDname,
{
fn partial_cmp(&self, other: &Mx<NN>) -> Option<Ordering> {
match self.preference.partial_cmp(&other.preference) {
Some(Ordering::Equal) => {}
other => return other,
}
Some(self.exchange.name_cmp(&other.exchange))
}
}
impl<N: ToDname> Ord for Mx<N> {
fn cmp(&self, other: &Self) -> Ordering {
match self.preference.cmp(&other.preference) {
Ordering::Equal => {}
other => return other,
}
self.exchange.name_cmp(&other.exchange)
}
}
impl<N: ToDname, NN: ToDname> CanonicalOrd<Mx<NN>> for Mx<N> {
fn canonical_cmp(&self, other: &Mx<NN>) -> Ordering {
match self.preference.cmp(&other.preference) {
Ordering::Equal => {}
other => return other,
}
self.exchange.lowercase_composed_cmp(&other.exchange)
}
}
impl<Ref: OctetsRef> Parse<Ref> for Mx<ParsedDname<Ref>> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
Ok(Self::new(u16::parse(parser)?, ParsedDname::parse(parser)?))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
u16::skip(parser)?;
ParsedDname::skip(parser)
}
}
impl<N: ToDname> Compose for Mx<N> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
self.preference.compose(target)?;
target.append_compressed_dname(&self.exchange)
})
}
fn compose_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
self.preference.compose(target)?;
self.exchange.compose_canonical(target)
})
}
}
#[cfg(feature = "master")]
impl<N: Scan> Scan for Mx<N> {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
Ok(Self::new(u16::scan(scanner)?, N::scan(scanner)?))
}
}
impl<N: fmt::Display> fmt::Display for Mx<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}.", self.preference, self.exchange)
}
}
impl<N> RtypeRecordData for Mx<N> {
const RTYPE: Rtype = Rtype::Mx;
}
dname_type! {
(Ns, Ns, nsdname)
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Null<Octets> {
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "crate::base::octets::SerializeOctets::serialize_octets",
deserialize_with = "crate::base::octets::DeserializeOctets::deserialize_octets",
bound(
serialize = "Octets: crate::base::octets::SerializeOctets",
deserialize = "Octets: crate::base::octets::DeserializeOctets<'de>",
)
)
)]
data: Octets,
}
impl<Octets> Null<Octets> {
pub fn new(data: Octets) -> Self {
Null { data }
}
pub fn data(&self) -> &Octets {
&self.data
}
}
impl<Octets: AsRef<[u8]>> Null<Octets> {
pub fn len(&self) -> usize {
self.data.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.data.as_ref().is_empty()
}
}
impl<SrcOctets> Null<SrcOctets> {
pub fn flatten_into<Octets>(self) -> Result<Null<Octets>, PushError>
where
Octets: OctetsFrom<SrcOctets>,
{
Ok(Null::new(self.data.octets_into()?))
}
}
impl<Octets> From<Octets> for Null<Octets> {
fn from(data: Octets) -> Self {
Self::new(data)
}
}
impl<Octets, SrcOctets> OctetsFrom<Null<SrcOctets>> for Null<Octets>
where
Octets: OctetsFrom<SrcOctets>,
{
fn octets_from(source: Null<SrcOctets>) -> Result<Self, ShortBuf> {
Octets::octets_from(source.data).map(Self::new)
}
}
impl<Octets, Other> PartialEq<Null<Other>> for Null<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn eq(&self, other: &Null<Other>) -> bool {
self.data.as_ref().eq(other.data.as_ref())
}
}
impl<Octets: AsRef<[u8]>> Eq for Null<Octets> {}
impl<Octets, Other> PartialOrd<Null<Other>> for Null<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &Null<Other>) -> Option<Ordering> {
self.data.as_ref().partial_cmp(other.data.as_ref())
}
}
impl<Octets, Other> CanonicalOrd<Null<Other>> for Null<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn canonical_cmp(&self, other: &Null<Other>) -> Ordering {
self.data.as_ref().cmp(other.data.as_ref())
}
}
impl<Octets: AsRef<[u8]>> Ord for Null<Octets> {
fn cmp(&self, other: &Self) -> Ordering {
self.data.as_ref().cmp(other.data.as_ref())
}
}
impl<Octets: AsRef<[u8]>> hash::Hash for Null<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.data.as_ref().hash(state)
}
}
impl<Ref: OctetsRef> Parse<Ref> for Null<Ref::Range> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
let len = parser.remaining();
parser.parse_octets(len).map(Self::new)
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
parser.advance_to_end();
Ok(())
}
}
impl<Octets: AsRef<[u8]>> Compose for Null<Octets> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_slice(self.data.as_ref())
}
}
impl<Octets> RtypeRecordData for Null<Octets> {
const RTYPE: Rtype = Rtype::Null;
}
impl<Octets> ops::Deref for Null<Octets> {
type Target = Octets;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl<Octets: AsRef<Other>, Other> AsRef<Other> for Null<Octets> {
fn as_ref(&self) -> &Other {
self.data.as_ref()
}
}
impl<Octets: AsRef<[u8]>> fmt::Display for Null<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\\# {}", self.data.as_ref().len())?;
for ch in self.data.as_ref().iter() {
write!(f, " {:02x}", ch)?;
}
Ok(())
}
}
impl<Octets: AsRef<[u8]>> fmt::Debug for Null<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Null(")?;
fmt::Display::fmt(self, f)?;
f.write_str(")")
}
}
dname_type! {
(Ptr, Ptr, ptrdname)
}
impl<N> Ptr<N> {
pub fn into_ptrdname(self) -> N {
self.ptrdname
}
}
#[derive(Clone, Debug, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Soa<N> {
mname: N,
rname: N,
serial: Serial,
refresh: u32,
retry: u32,
expire: u32,
minimum: u32,
}
impl<N> Soa<N> {
pub fn new(
mname: N,
rname: N,
serial: Serial,
refresh: u32,
retry: u32,
expire: u32,
minimum: u32,
) -> Self {
Soa {
mname,
rname,
serial,
refresh,
retry,
expire,
minimum,
}
}
pub fn mname(&self) -> &N {
&self.mname
}
pub fn rname(&self) -> &N {
&self.rname
}
pub fn serial(&self) -> Serial {
self.serial
}
pub fn refresh(&self) -> u32 {
self.refresh
}
pub fn retry(&self) -> u32 {
self.retry
}
pub fn expire(&self) -> u32 {
self.expire
}
pub fn minimum(&self) -> u32 {
self.minimum
}
}
impl<Ref> Soa<ParsedDname<Ref>>
where
Ref: OctetsRef,
{
pub fn flatten_into<Octets>(self) -> Result<Soa<Dname<Octets>>, PushError>
where
Octets: OctetsFrom<Ref::Range> + FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
{
let Self {
mname,
rname,
serial,
refresh,
retry,
expire,
minimum,
} = self;
Ok(Soa::new(
mname.flatten_into()?,
rname.flatten_into()?,
serial,
refresh,
retry,
expire,
minimum,
))
}
}
impl<Name, SrcName> OctetsFrom<Soa<SrcName>> for Soa<Name>
where
Name: OctetsFrom<SrcName>,
{
fn octets_from(source: Soa<SrcName>) -> Result<Self, ShortBuf> {
Ok(Soa::new(
Name::octets_from(source.mname)?,
Name::octets_from(source.rname)?,
source.serial,
source.refresh,
source.retry,
source.expire,
source.minimum,
))
}
}
impl<N, NN> PartialEq<Soa<NN>> for Soa<N>
where
N: ToDname,
NN: ToDname,
{
fn eq(&self, other: &Soa<NN>) -> bool {
self.mname.name_eq(&other.mname)
&& self.rname.name_eq(&other.rname)
&& self.serial == other.serial
&& self.refresh == other.refresh
&& self.retry == other.retry
&& self.expire == other.expire
&& self.minimum == other.minimum
}
}
impl<N: ToDname> Eq for Soa<N> {}
impl<N, NN> PartialOrd<Soa<NN>> for Soa<N>
where
N: ToDname,
NN: ToDname,
{
fn partial_cmp(&self, other: &Soa<NN>) -> Option<Ordering> {
match self.mname.name_cmp(&other.mname) {
Ordering::Equal => {}
other => return Some(other),
}
match self.rname.name_cmp(&other.rname) {
Ordering::Equal => {}
other => return Some(other),
}
match u32::from(self.serial).partial_cmp(&u32::from(other.serial)) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.refresh.partial_cmp(&other.refresh) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.retry.partial_cmp(&other.retry) {
Some(Ordering::Equal) => {}
other => return other,
}
match self.expire.partial_cmp(&other.expire) {
Some(Ordering::Equal) => {}
other => return other,
}
self.minimum.partial_cmp(&other.minimum)
}
}
impl<N: ToDname> Ord for Soa<N> {
fn cmp(&self, other: &Self) -> Ordering {
match self.mname.name_cmp(&other.mname) {
Ordering::Equal => {}
other => return other,
}
match self.rname.name_cmp(&other.rname) {
Ordering::Equal => {}
other => return other,
}
match u32::from(self.serial).cmp(&u32::from(other.serial)) {
Ordering::Equal => {}
other => return other,
}
match self.refresh.cmp(&other.refresh) {
Ordering::Equal => {}
other => return other,
}
match self.retry.cmp(&other.retry) {
Ordering::Equal => {}
other => return other,
}
match self.expire.cmp(&other.expire) {
Ordering::Equal => {}
other => return other,
}
self.minimum.cmp(&other.minimum)
}
}
impl<N: ToDname, NN: ToDname> CanonicalOrd<Soa<NN>> for Soa<N> {
fn canonical_cmp(&self, other: &Soa<NN>) -> Ordering {
match self.mname.lowercase_composed_cmp(&other.mname) {
Ordering::Equal => {}
other => return other,
}
match self.rname.lowercase_composed_cmp(&other.rname) {
Ordering::Equal => {}
other => return other,
}
match self.serial.canonical_cmp(&other.serial) {
Ordering::Equal => {}
other => return other,
}
match self.refresh.cmp(&other.refresh) {
Ordering::Equal => {}
other => return other,
}
match self.retry.cmp(&other.retry) {
Ordering::Equal => {}
other => return other,
}
match self.expire.cmp(&other.expire) {
Ordering::Equal => {}
other => return other,
}
self.minimum.cmp(&other.minimum)
}
}
impl<Ref: OctetsRef> Parse<Ref> for Soa<ParsedDname<Ref>> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
Ok(Self::new(
ParsedDname::parse(parser)?,
ParsedDname::parse(parser)?,
Serial::parse(parser)?,
u32::parse(parser)?,
u32::parse(parser)?,
u32::parse(parser)?,
u32::parse(parser)?,
))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
ParsedDname::skip(parser)?;
ParsedDname::skip(parser)?;
Serial::skip(parser)?;
u32::skip(parser)?;
u32::skip(parser)?;
u32::skip(parser)?;
u32::skip(parser)?;
Ok(())
}
}
impl<N: ToDname> Compose for Soa<N> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|buf| {
buf.append_compressed_dname(&self.mname)?;
buf.append_compressed_dname(&self.rname)?;
self.serial.compose(buf)?;
self.refresh.compose(buf)?;
self.retry.compose(buf)?;
self.expire.compose(buf)?;
self.minimum.compose(buf)
})
}
fn compose_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|buf| {
self.mname.compose_canonical(buf)?;
self.rname.compose_canonical(buf)?;
self.serial.compose(buf)?;
self.refresh.compose(buf)?;
self.retry.compose(buf)?;
self.expire.compose(buf)?;
self.minimum.compose(buf)
})
}
}
#[cfg(feature = "master")]
impl<N: Scan> Scan for Soa<N> {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
Ok(Self::new(
N::scan(scanner)?,
N::scan(scanner)?,
Serial::scan(scanner)?,
u32::scan(scanner)?,
u32::scan(scanner)?,
u32::scan(scanner)?,
u32::scan(scanner)?,
))
}
}
impl<N: fmt::Display> fmt::Display for Soa<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}. {}. {} {} {} {} {}",
self.mname,
self.rname,
self.serial,
self.refresh,
self.retry,
self.expire,
self.minimum
)
}
}
impl<N> RtypeRecordData for Soa<N> {
const RTYPE: Rtype = Rtype::Soa;
}
#[derive(Clone)]
pub struct Txt<Octets>(Octets);
impl<Octets: FromBuilder> Txt<Octets> {
pub fn from_slice(text: &[u8]) -> Result<Self, ShortBuf>
where
<Octets as FromBuilder>::Builder: EmptyBuilder + AsMut<[u8]>,
{
let mut builder = TxtBuilder::<Octets::Builder>::new();
builder.append_slice(text)?;
Ok(builder.finish())
}
}
impl<Octets: AsRef<[u8]>> Txt<Octets> {
pub fn from_octets(octets: Octets) -> Result<Self, CharStrError> {
let mut tmp = octets.as_ref();
while !tmp.is_empty() {
if tmp.len() <= tmp[0] as usize {
return Err(CharStrError);
}
tmp = &tmp[(tmp[0] as usize) + 1..];
}
Ok(Txt(octets))
}
pub fn iter(&self) -> TxtIter {
TxtIter(self.iter_char_strs())
}
pub fn iter_char_strs(&self) -> TxtCharStrIter {
TxtCharStrIter(Parser::from_ref(self.0.as_ref()))
}
pub fn as_flat_slice(&self) -> Option<&[u8]> {
if self.0.as_ref()[0] as usize == self.0.as_ref().len() - 1 {
Some(&self.0.as_ref()[1..])
} else {
None
}
}
pub fn len(&self) -> usize {
self.0.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.0.as_ref().is_empty()
}
pub fn text<T: FromBuilder>(&self) -> Result<T, ShortBuf>
where
<T as FromBuilder>::Builder: EmptyBuilder,
{
let mut res = T::Builder::with_capacity(self.len());
for item in self.iter() {
res.append_slice(item)?;
}
Ok(res.freeze())
}
}
impl<SrcOctets> Txt<SrcOctets> {
pub fn flatten_into<Octets>(self) -> Result<Txt<Octets>, PushError>
where
Octets: OctetsFrom<SrcOctets>,
{
Ok(Txt(self.0.octets_into()?))
}
}
impl<Octets, SrcOctets> OctetsFrom<Txt<SrcOctets>> for Txt<Octets>
where
Octets: OctetsFrom<SrcOctets>,
{
fn octets_from(source: Txt<SrcOctets>) -> Result<Self, ShortBuf> {
Octets::octets_from(source.0).map(Self)
}
}
impl<'a, Octets: AsRef<[u8]>> IntoIterator for &'a Txt<Octets> {
type Item = &'a [u8];
type IntoIter = TxtIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<Octets, Other> PartialEq<Txt<Other>> for Txt<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn eq(&self, other: &Txt<Other>) -> bool {
self.iter()
.flat_map(|s| s.iter().copied())
.eq(other.iter().flat_map(|s| s.iter().copied()))
}
}
impl<Octets: AsRef<[u8]>> Eq for Txt<Octets> {}
impl<Octets, Other> PartialOrd<Txt<Other>> for Txt<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn partial_cmp(&self, other: &Txt<Other>) -> Option<Ordering> {
self.iter()
.flat_map(|s| s.iter().copied())
.partial_cmp(other.iter().flat_map(|s| s.iter().copied()))
}
}
impl<Octets, Other> CanonicalOrd<Txt<Other>> for Txt<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn canonical_cmp(&self, other: &Txt<Other>) -> Ordering {
for (a, b) in self.iter().zip(other.iter()) {
match (a.len(), a).cmp(&(b.len(), b)) {
Ordering::Equal => continue,
r => return r,
}
}
Ordering::Equal
}
}
impl<Octets: AsRef<[u8]>> Ord for Txt<Octets> {
fn cmp(&self, other: &Self) -> Ordering {
self.iter()
.flat_map(|s| s.iter().copied())
.cmp(other.iter().flat_map(|s| s.iter().copied()))
}
}
impl<Octets: AsRef<[u8]>> hash::Hash for Txt<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.iter()
.flat_map(|s| s.iter().copied())
.for_each(|c| c.hash(state))
}
}
impl<Ref: OctetsRef> Parse<Ref> for Txt<Ref::Range> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
let len = parser.remaining();
let text = parser.parse_octets(len)?;
let mut tmp = Parser::from_ref(text.as_ref());
while tmp.remaining() != 0 {
CharStr::skip(&mut tmp)?
}
Ok(Txt(text))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
parser.advance_to_end();
Ok(())
}
}
impl<Octets: AsRef<[u8]>> Compose for Txt<Octets> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_slice(self.0.as_ref())
}
}
#[cfg(feature = "master")]
impl Scan for Txt<Bytes> {
fn scan<C: CharSource>(
scanner: &mut Scanner<C>,
) -> Result<Self, ScanError> {
scanner.scan_byte_phrase(|res| {
let mut builder = TxtBuilder::new_bytes();
if builder.append_slice(res.as_ref()).is_err() {
Err(SyntaxError::LongCharStr)
} else {
Ok(builder.finish())
}
})
}
}
impl<Octets: AsRef<[u8]>> fmt::Display for Txt<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for slice in self.iter() {
for ch in slice.iter() {
fmt::Display::fmt(&Symbol::from_octet(*ch), f)?
}
}
Ok(())
}
}
impl<Octets: AsRef<[u8]>> fmt::Debug for Txt<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Txt(")?;
fmt::Display::fmt(self, f)?;
f.write_str(")")
}
}
impl<Octets> RtypeRecordData for Txt<Octets> {
const RTYPE: Rtype = Rtype::Txt;
}
#[cfg(feature = "serde")]
impl<Octets> serde::Serialize for Txt<Octets>
where
Octets: AsRef<[u8]> + SerializeOctets,
{
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq;
struct TxtSeq<'a, Octets>(&'a Txt<Octets>);
impl<'a, Octets> serde::Serialize for TxtSeq<'a, Octets>
where
Octets: AsRef<[u8]> + SerializeOctets,
{
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut serializer = serializer.serialize_seq(None)?;
for item in self.0.iter_char_strs() {
serializer
.serialize_element(&format_args!("{}", item))?;
}
serializer.end()
}
}
if serializer.is_human_readable() {
serializer.serialize_newtype_struct("Txt", &TxtSeq(self))
} else {
serializer.serialize_newtype_struct(
"Txt",
&self.0.as_serialized_octets(),
)
}
}
}
#[cfg(feature = "serde")]
impl<'de, Octets> serde::Deserialize<'de> for Txt<Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder: EmptyBuilder + AsMut<[u8]>,
{
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use core::marker::PhantomData;
struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
impl<'de, Octets> serde::de::Visitor<'de> for InnerVisitor<'de, Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder:
OctetsBuilder<Octets = Octets> + EmptyBuilder + AsMut<[u8]>,
{
type Value = Txt<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("TXT record data")
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> Result<Self::Value, E> {
let mut builder =
TxtBuilder::<<Octets as FromBuilder>::Builder>::new();
let mut chars = v.chars();
while let Some(ch) =
Symbol::from_chars(&mut chars).map_err(E::custom)?
{
builder
.append_u8(ch.into_octet().map_err(E::custom)?)
.map_err(E::custom)?;
}
Ok(builder.finish())
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut seq: A,
) -> Result<Self::Value, A::Error> {
let mut builder =
TxtBuilder::<<Octets as FromBuilder>::Builder>::new();
while let Some(s) = seq.next_element::<&'de str>()? {
builder.append_zone_char_str(s)?;
}
Ok(builder.finish())
}
fn visit_borrowed_bytes<E: serde::de::Error>(
self,
value: &'de [u8],
) -> Result<Self::Value, E> {
self.0.visit_borrowed_bytes(value).and_then(|octets| {
Txt::from_octets(octets).map_err(E::custom)
})
}
#[cfg(feature = "std")]
fn visit_byte_buf<E: serde::de::Error>(
self,
value: std::vec::Vec<u8>,
) -> Result<Self::Value, E> {
self.0.visit_byte_buf(value).and_then(|octets| {
Txt::from_octets(octets).map_err(E::custom)
})
}
}
struct NewtypeVisitor<T>(PhantomData<T>);
impl<'de, Octets> serde::de::Visitor<'de> for NewtypeVisitor<Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder:
OctetsBuilder<Octets = Octets> + EmptyBuilder + AsMut<[u8]>,
{
type Value = Txt<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("TXT record data")
}
fn visit_newtype_struct<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
if deserializer.is_human_readable() {
deserializer
.deserialize_str(InnerVisitor(Octets::visitor()))
} else {
Octets::deserialize_with_visitor(
deserializer,
InnerVisitor(Octets::visitor()),
)
}
}
}
deserializer
.deserialize_newtype_struct("Txt", NewtypeVisitor(PhantomData))
}
}
#[derive(Clone)]
pub struct TxtCharStrIter<'a>(Parser<&'a [u8]>);
impl<'a> Iterator for TxtCharStrIter<'a> {
type Item = CharStr<&'a [u8]>;
fn next(&mut self) -> Option<Self::Item> {
if self.0.remaining() == 0 {
None
} else {
Some(CharStr::parse(&mut self.0).unwrap())
}
}
}
#[derive(Clone)]
pub struct TxtIter<'a>(TxtCharStrIter<'a>);
impl<'a> Iterator for TxtIter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(CharStr::into_octets)
}
}
#[derive(Clone, Debug)]
pub struct TxtBuilder<Builder> {
builder: Builder,
start: Option<usize>,
}
impl<Builder: OctetsBuilder + EmptyBuilder> TxtBuilder<Builder> {
pub fn new() -> Self {
TxtBuilder {
builder: Builder::empty(),
start: None,
}
}
}
#[cfg(feature = "bytes")]
impl TxtBuilder<BytesMut> {
pub fn new_bytes() -> Self {
Self::new()
}
}
impl<Builder: OctetsBuilder + AsMut<[u8]>> TxtBuilder<Builder> {
pub fn append_slice(&mut self, mut slice: &[u8]) -> Result<(), ShortBuf> {
if let Some(start) = self.start {
let left = 255 - (self.builder.len() - (start + 1));
if slice.len() < left {
self.builder.append_slice(slice)?;
return Ok(());
}
let (append, left) = slice.split_at(left);
self.builder.append_slice(append)?;
self.builder.as_mut()[start] = 255;
slice = left;
}
for chunk in slice.chunks(255) {
if self.builder.len() + chunk.len() + 1 >= 0xFFFF {
return Err(ShortBuf);
}
self.start = if chunk.len() == 255 {
None
} else {
Some(self.builder.len())
};
self.builder.append_slice(&[chunk.len() as u8])?;
self.builder.append_slice(chunk)?;
}
Ok(())
}
pub fn append_u8(&mut self, ch: u8) -> Result<(), ShortBuf> {
self.append_slice(&[ch])
}
#[cfg(feature = "serde")]
fn append_zone_char_str<E: serde::de::Error>(
&mut self,
s: &str,
) -> Result<(), E> {
self.close_char_str();
self.start = Some(self.builder.len());
self.builder.append_slice(&[0]).map_err(E::custom)?;
let mut chars = s.chars();
let mut len = 0;
while let Some(sym) =
Symbol::from_chars(&mut chars).map_err(E::custom)?
{
if len == 255 {
return Err(E::custom(CharStrError));
}
let sym = sym.into_octet().map_err(E::custom)?;
self.builder.append_slice(&[sym]).map_err(E::custom)?;
len += 1;
}
self.close_char_str();
Ok(())
}
fn close_char_str(&mut self) {
if let Some(start) = self.start {
let last_slice_len = self.builder.len() - (start + 1);
self.builder.as_mut()[start] = last_slice_len as u8;
}
}
pub fn finish(mut self) -> Txt<Builder::Octets> {
self.close_char_str();
Txt(self.builder.freeze())
}
}
impl<Builder: OctetsBuilder + EmptyBuilder> Default for TxtBuilder<Builder> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
#[cfg(feature = "std")]
mod test {
use super::*;
use std::vec::Vec;
#[test]
#[cfg(features = "bytes")]
fn hinfo_octets_into() {
use crate::octets::OctetsInto;
let hinfo: Hinfo<Vec<u8>> =
Hinfo::new("1234".parse().unwrap(), "abcd".parse().unwrap());
let hinfo_bytes: Hinfo<bytes::Bytes> = hinfo.octets_into().unwrap();
assert_eq!(hinfo.cpu(), hinfo_bytes.cpu());
assert_eq!(hinfo.os(), hinfo_bytes.os());
}
#[test]
#[cfg(features = "bytes")]
fn minfo_octets_into() {
use crate::base::Dname;
use crate::octets::OctetsInto;
let minfo: Minfo<Dname<Vec<u8>>> = Minfo::new(
"a.example".parse().unwrap(),
"b.example".parse().unwrap(),
);
let minfo_bytes: Minfo<Dname<bytes::Bytes>> =
minfo.octets_into().unwrap();
assert_eq!(minfo.rmailbx(), minfo_bytes.rmailbx());
assert_eq!(minfo.emailbx(), minfo_bytes.emailbx());
}
#[test]
fn txt_from_slice() {
let short = b"01234";
let txt: Txt<Vec<u8>> = Txt::from_slice(short).unwrap();
assert_eq!(Some(&short[..]), txt.as_flat_slice());
assert_eq!(Ok(short.to_vec()), txt.text::<Vec<u8>>());
let full = short.repeat(51);
let txt: Txt<Vec<u8>> = Txt::from_slice(&full).unwrap();
assert_eq!(Some(&full[..]), txt.as_flat_slice());
assert_eq!(Ok(full.to_vec()), txt.text::<Vec<u8>>());
let long = short.repeat(52);
let txt: Txt<Vec<u8>> = Txt::from_slice(&long).unwrap();
assert_eq!(None, txt.as_flat_slice());
assert_eq!(Ok(long.to_vec()), txt.text::<Vec<u8>>());
let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
for chunk in long.chunks(9) {
builder.append_slice(chunk).unwrap();
}
let txt = builder.finish();
assert_eq!(None, txt.as_flat_slice());
assert_eq!(Ok(long.to_vec()), txt.text::<Vec<u8>>());
let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
assert!(builder.append_slice(&[]).is_ok());
let empty = builder.finish();
assert!(empty.is_empty());
assert_eq!(0, empty.iter().count());
let mut parser = Parser::from_static(b"\x01");
assert!(Txt::parse(&mut parser).is_err());
let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
assert!(builder
.append_slice(&b"\x00".repeat(std::u16::MAX as usize))
.is_err());
let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
assert!(builder
.append_slice(&b"\x00".repeat(std::u16::MAX as usize - 512))
.is_ok());
assert!(builder.append_slice(&b"\x00".repeat(512)).is_err());
}
#[test]
fn txt_canonical_compare() {
let data = [
"mailru-verification: 14505c6eb222c847",
"yandex-verification: 6059b187e78de544",
"v=spf1 include:_spf.protonmail.ch ~all",
"swisssign-check=CF0JHMTlTDNoES3rrknIRggocffSwqmzMb9X8YbjzK",
"google-site-verification=aq9zJnp3H3bNE0Y4D4rH5I5Dhj8VMaLYx0uQ7Rozfgg",
"ahrefs-site-verification_4bdac6bbaa81e0d591d7c0f3ef238905c0521b69bf3d74e64d3775bcb2743afd",
"brave-ledger-verification=66a7f27fb99949cc0c564ab98efcc58ea1bac3e97eb557c782ab2d44b49aefd7",
];
let records = data
.iter()
.map(|e| {
let mut builder = TxtBuilder::<Vec<u8>>::new();
builder.append_slice(e.as_bytes()).unwrap();
builder.finish()
})
.collect::<Vec<_>>();
let mut sorted = records.clone();
sorted.sort_by(|a, b| a.canonical_cmp(b));
for (a, b) in records.iter().zip(sorted.iter()) {
assert_eq!(a, b);
}
}
#[cfg(all(feature = "serde", feature = "std"))]
#[test]
fn txt_ser_de() {
use serde_test::{assert_tokens, Configure, Token};
let txt = Txt::from_octets(Vec::from(b"\x03foo".as_ref())).unwrap();
assert_tokens(
&txt.clone().compact(),
&[
Token::NewtypeStruct { name: "Txt" },
Token::ByteBuf(b"\x03foo"),
],
);
assert_tokens(
&txt.readable(),
&[
Token::NewtypeStruct { name: "Txt" },
Token::Seq { len: None },
Token::BorrowedStr("foo"),
Token::SeqEnd,
],
);
}
#[cfg(all(feature = "serde", feature = "std"))]
#[test]
fn txt_de_str() {
use serde_test::{assert_de_tokens, Configure, Token};
assert_de_tokens(
&Txt::from_octets(Vec::from(b"\x03foo".as_ref()))
.unwrap()
.readable(),
&[
Token::NewtypeStruct { name: "Txt" },
Token::BorrowedStr("foo"),
],
);
}
}