#![allow(clippy::use_self)]
use alloc::borrow::ToOwned;
use core::{
cmp::Ordering,
fmt::{self, Display, Formatter},
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::serialize::binary::*;
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
pub enum DNSClass {
IN,
CH,
HS,
NONE,
ANY,
OPT(u16),
Unknown(u16),
}
impl FromStr for DNSClass {
type Err = DecodeError;
fn from_str(str: &str) -> Result<Self, Self::Err> {
debug_assert!(str.chars().all(|x| !char::is_ascii_lowercase(&x)));
match str {
"IN" => Ok(Self::IN),
"CH" => Ok(Self::CH),
"HS" => Ok(Self::HS),
"NONE" => Ok(Self::NONE),
"ANY" | "*" => Ok(Self::ANY),
_ => Err(DecodeError::UnknownDnsClassStr(str.to_owned())),
}
}
}
impl DNSClass {
pub fn for_opt(value: u16) -> Self {
let value = value.max(512);
Self::OPT(value)
}
}
impl BinEncodable for DNSClass {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u16((*self).into())
}
}
impl BinDecodable<'_> for DNSClass {
fn read(decoder: &mut BinDecoder<'_>) -> Result<Self, DecodeError> {
let this = Self::from(
decoder.read_u16()?.unverified(),
);
Ok(this)
}
}
impl From<DNSClass> for &'static str {
fn from(rt: DNSClass) -> &'static str {
match rt {
DNSClass::IN => "IN",
DNSClass::CH => "CH",
DNSClass::HS => "HS",
DNSClass::NONE => "NONE",
DNSClass::ANY => "ANY",
DNSClass::OPT(_) => "OPT",
DNSClass::Unknown(_) => "UNKNOWN",
}
}
}
impl From<u16> for DNSClass {
fn from(value: u16) -> Self {
match value {
1 => Self::IN,
3 => Self::CH,
4 => Self::HS,
254 => Self::NONE,
255 => Self::ANY,
_ => Self::Unknown(value),
}
}
}
impl From<DNSClass> for u16 {
fn from(rt: DNSClass) -> Self {
match rt {
DNSClass::IN => 1,
DNSClass::CH => 3,
DNSClass::HS => 4,
DNSClass::NONE => 254,
DNSClass::ANY => 255,
DNSClass::OPT(max_payload_len) => max_payload_len.max(512),
DNSClass::Unknown(unknown) => unknown,
}
}
}
impl PartialOrd<Self> for DNSClass {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DNSClass {
fn cmp(&self, other: &Self) -> Ordering {
u16::from(*self).cmp(&u16::from(*other))
}
}
impl Display for DNSClass {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(Into::<&str>::into(*self))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_order() {
let ordered = vec![
DNSClass::IN,
DNSClass::CH,
DNSClass::HS,
DNSClass::NONE,
DNSClass::ANY,
];
let mut unordered = vec![
DNSClass::NONE,
DNSClass::HS,
DNSClass::CH,
DNSClass::IN,
DNSClass::ANY,
];
unordered.sort();
assert_eq!(unordered, ordered);
}
#[test]
fn check_dns_class_parse_wont_panic_with_symbols() {
let dns_class = "a-b-c".to_ascii_uppercase().parse::<DNSClass>();
assert!(matches!(
&dns_class,
Err(DecodeError::UnknownDnsClassStr(_))
));
}
}