use super::super::iana::OptionCode;
use super::super::message_builder::OptBuilder;
use super::super::wire::{Composer, ParseError};
use super::{
BuildDataError, ComposeOptData, LongOptData, Opt, OptData, ParseOptData,
};
use core::cmp::Ordering;
use core::{borrow, fmt, hash, mem, str};
use octseq::builder::OctetsBuilder;
use octseq::octets::{Octets, OctetsFrom};
use octseq::parse::Parser;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Nsid<Octs: ?Sized> {
octets: Octs,
}
impl Nsid<()> {
pub(super) const CODE: OptionCode = OptionCode::NSID;
}
#[cfg(feature = "serde")]
impl<Octs: octseq::serde::SerializeOctets> serde::Serialize for Nsid<Octs> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.octets.serialize_octets(serializer)
}
}
impl<Octs> Nsid<Octs> {
pub fn from_octets(octets: Octs) -> Result<Self, LongOptData>
where
Octs: AsRef<[u8]>,
{
LongOptData::check_len(octets.as_ref().len())?;
Ok(unsafe { Self::from_octets_unchecked(octets) })
}
pub unsafe fn from_octets_unchecked(octets: Octs) -> Self {
Nsid { octets }
}
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
parser: &mut Parser<'a, Src>,
) -> Result<Self, ParseError> {
let len = parser.remaining();
LongOptData::check_len(len)?;
Ok(unsafe { Self::from_octets_unchecked(parser.parse_octets(len)?) })
}
}
impl Nsid<[u8]> {
pub fn from_slice(slice: &[u8]) -> Result<&Self, LongOptData> {
LongOptData::check_len(slice.len())?;
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
#[must_use]
pub unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
mem::transmute(slice)
}
#[must_use]
pub fn empty() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"") }
}
}
impl<Octs: ?Sized> Nsid<Octs> {
pub fn as_octets(&self) -> &Octs {
&self.octets
}
pub fn into_octets(self) -> Octs
where
Octs: Sized,
{
self.octets
}
pub fn as_slice(&self) -> &[u8]
where
Octs: AsRef<[u8]>,
{
self.octets.as_ref()
}
pub fn for_slice(&self) -> &Nsid<[u8]>
where
Octs: AsRef<[u8]>,
{
unsafe { Nsid::from_slice_unchecked(self.octets.as_ref()) }
}
}
impl<Octs, SrcOcts> OctetsFrom<Nsid<SrcOcts>> for Nsid<Octs>
where
Octs: OctetsFrom<SrcOcts>,
{
type Error = Octs::Error;
fn try_octets_from(src: Nsid<SrcOcts>) -> Result<Self, Self::Error> {
Octs::try_octets_from(src.octets)
.map(|octets| unsafe { Self::from_octets_unchecked(octets) })
}
}
impl<Octs: AsRef<[u8]> + ?Sized> AsRef<[u8]> for Nsid<Octs> {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octs: AsRef<[u8]> + ?Sized> borrow::Borrow<[u8]> for Nsid<Octs> {
fn borrow(&self) -> &[u8] {
self.as_slice()
}
}
impl<Octs: ?Sized> OptData for Nsid<Octs> {
fn code(&self) -> OptionCode {
OptionCode::NSID
}
}
impl<'a, Octs: Octets> ParseOptData<'a, Octs> for Nsid<Octs::Range<'a>> {
fn parse_option(
code: OptionCode,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
if code == OptionCode::NSID {
Self::parse(parser).map(Some)
} else {
Ok(None)
}
}
}
impl<Octs: AsRef<[u8]> + ?Sized> ComposeOptData for Nsid<Octs> {
fn compose_len(&self) -> u16 {
self.octets
.as_ref()
.len()
.try_into()
.expect("long option data")
}
fn compose_option<Target: OctetsBuilder + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
target.append_slice(self.octets.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Nsid<Octs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for v in self.octets.as_ref() {
write!(f, "{:X} ", *v)?;
}
if let Ok(s) = str::from_utf8(self.octets.as_ref()) {
write!(f, "({})", s)?;
}
Ok(())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Nsid<Octs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Nsid({})", self)
}
}
impl<Octs, Other> PartialEq<Other> for Nsid<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
Other: AsRef<[u8]> + ?Sized,
{
fn eq(&self, other: &Other) -> bool {
self.as_slice().eq(other.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Eq for Nsid<Octs> {}
impl<Octs, Other> PartialOrd<Other> for Nsid<Octs>
where
Octs: AsRef<[u8]> + ?Sized,
Other: AsRef<[u8]> + ?Sized,
{
fn partial_cmp(&self, other: &Other) -> Option<Ordering> {
self.as_slice().partial_cmp(other.as_ref())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> Ord for Nsid<Octs> {
fn cmp(&self, other: &Self) -> Ordering {
self.as_slice().cmp(other.as_slice())
}
}
impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Nsid<Octs> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_slice().hash(state)
}
}
impl<Octs: Octets> Opt<Octs> {
pub fn nsid(&self) -> Option<Nsid<Octs::Range<'_>>> {
self.first()
}
}
impl<Target: Composer> OptBuilder<'_, Target> {
pub fn nsid(
&mut self,
data: &(impl AsRef<[u8]> + ?Sized),
) -> Result<(), BuildDataError> {
Ok(self.push(Nsid::from_slice(data.as_ref())?)?)
}
pub fn client_nsid(&mut self) -> Result<(), Target::AppendError> {
self.push(Nsid::empty())
}
}
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
mod test {
use super::super::test::test_option_compose_parse;
use super::*;
#[test]
#[allow(clippy::redundant_closure)] fn nsid_compose_parse() {
test_option_compose_parse(
&Nsid::from_octets("foo").unwrap(),
|parser| Nsid::parse(parser),
);
}
}