1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use crate::{Error, Result};
use std::fmt::{self, Display, Formatter};

/// Response codes.
///
/// [RFC 1035 section 4.1.1](https://www.rfc-editor.org/rfc/rfc1035.html#section-4.1.1)
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum RCode {
    /// No error condition
    NoError = 0,
    /// Format error - the name server was unable to interpret the query.
    FormErr = 1,
    /// Server failure - the name server was unable to process this query
    /// due to a problem with the name server.
    ServFail = 2,
    /// Name error - domain name does not exist.
    NxDomain = 3,
    /// Not implemented - the name server doesn't support the requested kind of query.
    NotImp = 4,
    /// Refused - the name server refuses to perform the specified operation for policy reasons.
    Refused = 5,
}

impl RCode {
    /// Array of all discriminants in this enum.
    #[cfg(test)]
    pub const VALUES: [RCode; 6] = [
        RCode::NoError,
        RCode::FormErr,
        RCode::ServFail,
        RCode::NxDomain,
        RCode::NotImp,
        RCode::Refused,
    ];

    /// Converts an rcode to a static string.
    pub fn to_str(self) -> &'static str {
        match self {
            RCode::NoError => "NOERROR",
            RCode::FormErr => "FORMERR",
            RCode::ServFail => "SERVFAIL",
            RCode::NxDomain => "NXDOMAIN",
            RCode::NotImp => "NOTIMP",
            RCode::Refused => "REFUSED",
        }
    }

    pub(crate) fn try_from_u16(value: u16) -> Result<Self> {
        let me = match value {
            0 => RCode::NoError,
            1 => RCode::FormErr,
            2 => RCode::ServFail,
            3 => RCode::NxDomain,
            4 => RCode::NotImp,
            5 => RCode::Refused,
            _ => return Err(Error::UnrecognizedResponseCode(value.into())),
        };

        Ok(me)
    }
}

impl Display for RCode {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.pad(self.to_str())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::message::ResponseCode;

    #[test]
    fn test_try_from_u16() {
        for r_code in RCode::VALUES {
            assert_eq!(r_code, RCode::try_from_u16(r_code as u16).unwrap());
        }

        assert!(matches!(
            RCode::try_from_u16(128),
            Err(Error::UnrecognizedResponseCode(ResponseCode { value: 128 }))
        ));
    }

    #[test]
    fn test_values() {
        let mut count = 0;

        for rcode in RCode::VALUES {
            let found = match rcode {
                RCode::NoError => true,
                RCode::FormErr => true,
                RCode::ServFail => true,
                RCode::NxDomain => true,
                RCode::NotImp => true,
                RCode::Refused => true,
            };

            if found {
                count += 1;
            }
        }

        assert_eq!(count, RCode::VALUES.len());
    }
}