use std::cmp::Ordering;
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::serialize::binary::*;
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::rdata::DNSSECRecordType;
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
pub enum RecordType {
    
    A,
    
    AAAA,
    
    ANAME,
    
    
    ANY,
    
    
    AXFR,
    
    CAA,
    
    
    CNAME,
    
    
    
    HINFO,
    
    
    HTTPS,
    
    
    IXFR,
    
    
    
    MX,
    
    NAPTR,
    
    NS,
    
    NULL,
    
    OPENPGPKEY,
    
    OPT,
    
    PTR,
    
    
    SOA,
    
    SRV,
    
    SSHFP,
    
    SVCB,
    
    
    
    TLSA,
    
    
    TXT,
    
    
    
    
    #[cfg(feature = "dnssec")]
    DNSSEC(DNSSECRecordType),
    
    Unknown(u16),
    
    ZERO,
}
impl RecordType {
    
    #[inline]
    pub fn is_any(self) -> bool {
        self == RecordType::ANY
    }
    
    #[inline]
    pub fn is_cname(self) -> bool {
        self == RecordType::CNAME
    }
    
    #[inline]
    pub fn is_ns(self) -> bool {
        self == RecordType::NS
    }
    
    #[inline]
    pub fn is_soa(self) -> bool {
        self == RecordType::SOA
    }
    
    #[inline]
    pub fn is_srv(self) -> bool {
        self == RecordType::SRV
    }
    
    #[inline]
    pub fn is_ip_addr(self) -> bool {
        matches!(self, RecordType::A | RecordType::AAAA)
    }
}
impl FromStr for RecordType {
    type Err = ProtoError;
    
    
    
    
    
    
    
    
    
    fn from_str(str: &str) -> ProtoResult<Self> {
        
        debug_assert!(str.chars().all(|x| char::is_digit(x, 36)));
        match str {
            "A" => Ok(RecordType::A),
            "AAAA" => Ok(RecordType::AAAA),
            "ANAME" => Ok(RecordType::ANAME),
            "CAA" => Ok(RecordType::CAA),
            "CNAME" => Ok(RecordType::CNAME),
            "HINFO" => Ok(RecordType::HINFO),
            "HTTPS" => Ok(RecordType::HTTPS),
            "NULL" => Ok(RecordType::NULL),
            "MX" => Ok(RecordType::MX),
            "NAPTR" => Ok(RecordType::NAPTR),
            "NS" => Ok(RecordType::NS),
            "OPENPGPKEY" => Ok(RecordType::OPENPGPKEY),
            "PTR" => Ok(RecordType::PTR),
            "SOA" => Ok(RecordType::SOA),
            "SRV" => Ok(RecordType::SRV),
            "SSHFP" => Ok(RecordType::SSHFP),
            "SVCB" => Ok(RecordType::SVCB),
            "TLSA" => Ok(RecordType::TLSA),
            "TXT" => Ok(RecordType::TXT),
            "ANY" | "*" => Ok(RecordType::ANY),
            "AXFR" => Ok(RecordType::AXFR),
            #[cfg(feature = "dnssec")]
            "DNSKEY" | "DS" | "KEY" | "NSEC" | "NSEC3" | "NSEC3PARAM" | "RRSIG" | "SIG" => {
                Ok(RecordType::DNSSEC(str.parse()?))
            }
            _ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
        }
    }
}
impl From<u16> for RecordType {
    
    
    
    
    
    
    
    
    fn from(value: u16) -> Self {
        match value {
            1 => RecordType::A,
            28 => RecordType::AAAA,
            
            65305 => RecordType::ANAME,
            255 => RecordType::ANY,
            252 => RecordType::AXFR,
            257 => RecordType::CAA,
            5 => RecordType::CNAME,
            13 => RecordType::HINFO,
            65 => RecordType::HTTPS,
            15 => RecordType::MX,
            35 => RecordType::NAPTR,
            2 => RecordType::NS,
            10 => RecordType::NULL,
            61 => RecordType::OPENPGPKEY,
            41 => RecordType::OPT,
            12 => RecordType::PTR,
            6 => RecordType::SOA,
            33 => RecordType::SRV,
            44 => RecordType::SSHFP,
            64 => RecordType::SVCB,
            52 => RecordType::TLSA,
            16 => RecordType::TXT,
            0 => RecordType::ZERO,
            #[cfg(feature = "dnssec")]
            48 |
            43 |
            25 |
            47|
            50|
            51|
            46|
            24 => RecordType::DNSSEC(DNSSECRecordType::from(value)),
            
            _ => RecordType::Unknown(value),
        }
    }
}
impl BinEncodable for RecordType {
    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
        encoder.emit_u16((*self).into())
    }
}
impl<'r> BinDecodable<'r> for RecordType {
    fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
        Ok(decoder
            .read_u16()
            .map(
                Restrict::unverified, 
            )
            .map(Self::from)?)
    }
}
impl From<RecordType> for &'static str {
    fn from(rt: RecordType) -> &'static str {
        match rt {
            RecordType::A => "A",
            RecordType::AAAA => "AAAA",
            RecordType::ANAME => "ANAME",
            RecordType::ANY => "ANY",
            RecordType::AXFR => "AXFR",
            RecordType::CAA => "CAA",
            RecordType::CNAME => "CNAME",
            RecordType::HINFO => "HINFO",
            RecordType::HTTPS => "HTTPS",
            RecordType::ZERO => "ZERO",
            RecordType::IXFR => "IXFR",
            RecordType::MX => "MX",
            RecordType::NAPTR => "NAPTR",
            RecordType::NS => "NS",
            RecordType::NULL => "NULL",
            RecordType::OPENPGPKEY => "OPENPGPKEY",
            RecordType::OPT => "OPT",
            RecordType::PTR => "PTR",
            RecordType::SOA => "SOA",
            RecordType::SRV => "SRV",
            RecordType::SSHFP => "SSHFP",
            RecordType::SVCB => "SVCB",
            RecordType::TLSA => "TLSA",
            RecordType::TXT => "TXT",
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(_) => "Unknown",
        }
    }
}
impl From<RecordType> for u16 {
    fn from(rt: RecordType) -> Self {
        match rt {
            RecordType::A => 1,
            RecordType::AAAA => 28,
            
            RecordType::ANAME => 65305,
            RecordType::ANY => 255,
            RecordType::AXFR => 252,
            RecordType::CAA => 257,
            RecordType::CNAME => 5,
            RecordType::HINFO => 13,
            RecordType::HTTPS => 65,
            RecordType::ZERO => 0,
            RecordType::IXFR => 251,
            RecordType::MX => 15,
            RecordType::NAPTR => 35,
            RecordType::NS => 2,
            RecordType::NULL => 10,
            RecordType::OPENPGPKEY => 61,
            RecordType::OPT => 41,
            RecordType::PTR => 12,
            RecordType::SOA => 6,
            RecordType::SRV => 33,
            RecordType::SSHFP => 44,
            RecordType::SVCB => 64,
            RecordType::TLSA => 52,
            RecordType::TXT => 16,
            #[cfg(feature = "dnssec")]
            RecordType::DNSSEC(rt) => rt.into(),
            RecordType::Unknown(code) => code,
        }
    }
}
impl PartialOrd<RecordType> for RecordType {
    fn partial_cmp(&self, other: &RecordType) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}
impl Ord for RecordType {
    fn cmp(&self, other: &Self) -> Ordering {
        u16::from(*self).cmp(&u16::from(*other))
    }
}
impl Display for RecordType {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
        f.write_str(Into::<&str>::into(*self))
    }
}
#[cfg(test)]
mod tests {
    #![allow(clippy::dbg_macro, clippy::print_stdout)]
    use super::*;
    #[test]
    fn test_order() {
        let ordered = vec![
            RecordType::A,
            RecordType::NS,
            RecordType::CNAME,
            RecordType::SOA,
            RecordType::NULL,
            RecordType::PTR,
            RecordType::HINFO,
            RecordType::MX,
            RecordType::TXT,
            RecordType::AAAA,
            RecordType::SRV,
            RecordType::AXFR,
            RecordType::ANY,
        ];
        let mut unordered = vec![
            RecordType::ANY,
            RecordType::NULL,
            RecordType::AXFR,
            RecordType::A,
            RecordType::NS,
            RecordType::SOA,
            RecordType::SRV,
            RecordType::PTR,
            RecordType::MX,
            RecordType::CNAME,
            RecordType::TXT,
            RecordType::AAAA,
            RecordType::HINFO,
        ];
        unordered.sort();
        for rtype in unordered.clone() {
            println!("u16 for {:?}: {}", rtype, u16::from(rtype));
        }
        assert_eq!(ordered, unordered);
    }
    
    
    #[test]
    fn test_record_type_parse() {
        let record_names = &[
            "A",
            "AAAA",
            "ANAME",
            "CAA",
            "CNAME",
            "HINFO",
            "NULL",
            "MX",
            "NAPTR",
            "NS",
            "OPENPGPKEY",
            "PTR",
            "SOA",
            "SRV",
            "SSHFP",
            "TLSA",
            "TXT",
            "ANY",
            "AXFR",
        ];
        #[cfg(feature = "dnssec")]
        let dnssec_record_names = &[
            "DNSKEY",
            "DS",
            "KEY",
            "NSEC",
            "NSEC3",
            "NSEC3PARAM",
            "RRSIG",
            "SIG",
        ];
        #[cfg(not(feature = "dnssec"))]
        let dnssec_record_names = &[];
        let mut rtypes = std::collections::HashSet::new();
        for name in record_names.iter().chain(dnssec_record_names) {
            let rtype: RecordType = name.parse().unwrap();
            assert_eq!(rtype.to_string().to_ascii_uppercase().as_str(), *name);
            assert!(rtypes.insert(rtype));
        }
    }
}