use super::super::iana::OptionCode;
use super::super::message_builder::OptBuilder;
use super::super::name::{Name, ToName};
use super::super::wire::{Composer, ParseError};
use super::{ComposeOptData, Opt, OptData, ParseOptData};
use core::{fmt, hash, mem};
use core::cmp::Ordering;
use octseq::builder::OctetsBuilder;
use octseq::octets::{Octets, OctetsFrom};
use octseq::parse::Parser;
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[repr(transparent)]
pub struct Chain<Name: ?Sized> {
start: Name,
}
impl Chain<()> {
pub(super) const CODE: OptionCode = OptionCode::CHAIN;
}
impl<Name: ?Sized> Chain<Name> {
pub fn new(start: Name) -> Self
where
Name: Sized,
{
Chain { start }
}
pub fn new_ref(start: &Name) -> &Self {
unsafe { mem::transmute(start) }
}
pub fn start(&self) -> &Name {
&self.start
}
pub fn into_start(self) -> Name
where
Name: Sized,
{
self.start
}
}
impl<Octs> Chain<Name<Octs>> {
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
parser: &mut Parser<'a, Src>,
) -> Result<Self, ParseError> {
Name::parse(parser).map(Self::new)
}
}
impl<Name, SrcName> OctetsFrom<Chain<SrcName>> for Chain<Name>
where
Name: OctetsFrom<SrcName>,
{
type Error = Name::Error;
fn try_octets_from(src: Chain<SrcName>) -> Result<Self, Self::Error> {
Name::try_octets_from(src.start).map(Self::new)
}
}
impl<Name, OtherName> PartialEq<Chain<OtherName>> for Chain<Name>
where
Name: ToName,
OtherName: ToName,
{
fn eq(&self, other: &Chain<OtherName>) -> bool {
self.start().name_eq(other.start())
}
}
impl<Name: ToName> Eq for Chain<Name> {}
impl<Name, OtherName> PartialOrd<Chain<OtherName>> for Chain<Name>
where
Name: ToName,
OtherName: ToName,
{
fn partial_cmp(&self, other: &Chain<OtherName>) -> Option<Ordering> {
Some(self.start().name_cmp(other.start()))
}
}
impl<Name: ToName> Ord for Chain<Name> {
fn cmp(&self, other: &Self) -> Ordering {
self.start().name_cmp(other.start())
}
}
impl<Name: hash::Hash> hash::Hash for Chain<Name> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.start().hash(state)
}
}
impl<Name> OptData for Chain<Name> {
fn code(&self) -> OptionCode {
OptionCode::CHAIN
}
}
impl<'a, Octs> ParseOptData<'a, Octs> for Chain<Name<Octs::Range<'a>>>
where
Octs: Octets,
{
fn parse_option(
code: OptionCode,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
if code == OptionCode::CHAIN {
Self::parse(parser).map(Some)
} else {
Ok(None)
}
}
}
impl<Name: ToName> ComposeOptData for Chain<Name> {
fn compose_len(&self) -> u16 {
self.start.compose_len()
}
fn compose_option<Target: OctetsBuilder + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
self.start.compose(target)
}
}
impl<Name: fmt::Display> fmt::Display for Chain<Name> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.start)
}
}
impl<Name: fmt::Display> fmt::Debug for Chain<Name> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Chain")
.field("start", &format_args!("{}", self.start))
.finish()
}
}
impl<Octs: Octets> Opt<Octs> {
pub fn chain(&self) -> Option<Chain<Name<Octs::Range<'_>>>> {
self.first()
}
}
impl<'a, Target: Composer> OptBuilder<'a, Target> {
pub fn chain(
&mut self,
start: impl ToName,
) -> Result<(), Target::AppendError> {
self.push(&Chain::new(start))
}
}
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
mod test {
use super::super::test::test_option_compose_parse;
use super::*;
use core::str::FromStr;
use std::vec::Vec;
#[test]
#[allow(clippy::redundant_closure)] fn chain_compose_parse() {
test_option_compose_parse(
&Chain::new(Name::<Vec<u8>>::from_str("example.com").unwrap()),
|parser| Chain::parse(parser),
);
}
}