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
108
109
//! See: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a

/// The HRESULT numbering space is vendor-extensible. Vendors can supply their own values for this field, as long as the C bit (`0x20000000`) is set, indicating it is a customer code.
///
/// The HRESULT numbering space has the following internal structure. Any protocol that uses NTSTATUS values on the wire is responsible for stating the order in which the bytes are placed on the wire.
pub struct HRESULT(i32);

impl std::fmt::Debug for HRESULT {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        f.debug_struct("HRESULT")
            .field("s", &self.s()) // as 0/1?
            .field("r", &self.r()) // as 0/1?
            .field("c", &self.c()) // as 0/1?
            .field("n", &self.n()) // as 0/1?
            .field("x", &self.x()) // as 0/1?
            .field("facility", &self.facility()) // as hex?
            .field("code", &self.code()) // as hex?
            .finish()
    }
}

impl std::fmt::Display for HRESULT {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "{:#08X}", self.0)
    }
}

impl From<i32> for HRESULT {
    fn from(val: i32) -> Self {
        HRESULT(val)
    }
}

impl From<u32> for HRESULT {
    fn from(val: u32) -> Self {
        HRESULT(val as i32)
    }
}

impl HRESULT {
    pub fn is_success(&self) -> bool {
        self.s()
    }

    pub fn is_customer(&self) -> bool {
        self.c()
    }

    /// Severity. If set, indicates a failure result. If clear, indicates a success result.
    pub fn s(&self) -> bool {
        self.0 >= 0
    }

    /// Reserved. If the N bit is clear, this bit MUST be set to 0. If the N bit is set, this bit is defined by the NTSTATUS numbering space (as specified in section 2.3).
    pub fn r(&self) -> bool {
        self.0 & 0b0100_0000_0000_0000_0000_0000_0000_0000 != 0
    }

    /// Customer. This bit specifies if the value is customer-defined or Microsoft-defined. The bit is set for customer-defined values and clear for Microsoft-defined values. <1>
    pub fn c(&self) -> bool {
        self.0 & 0b0010_0000_0000_0000_0000_0000_0000_0000 != 0
    }

    /// If set, indicates that the error code is an NTSTATUS value (as specified in section 2.3), except that this bit is set.
    pub fn n(&self) -> bool {
        self.0 & 0b0001_0000_0000_0000_0000_0000_0000_0000 != 0
    }

    /// Reserved. SHOULD be set to 0. <2>
    pub fn x(&self) -> bool {
        self.0 & 0b0000_1000_0000_0000_0000_0000_0000_0000 != 0
    }

    /// An indicator of the source of the error. New facilities are occasionally added by Microsoft.
    pub fn facility(&self) -> u16 {
        (self.0 >> 16) as u16 & 0b0000_0111_1111_1111
    }

    pub fn code(&self) -> u16 {
        self.0 as u16
    }
}

#[allow(clippy::unreadable_literal)]
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn simple() {
        let hr: HRESULT = 0x80070005_u32.into();
        assert!(!hr.s());
        assert!(!hr.r());
        assert!(!hr.c());
        assert!(!hr.n());
        assert!(!hr.x());
        assert_eq!(hr.facility(), 0x7);
        assert_eq!(hr.code(), 0x5);

        let hr: HRESULT = 0x80004005_u32.into();
        assert!(!hr.s());
        assert!(!hr.r());
        assert!(!hr.c());
        assert!(!hr.n());
        assert!(!hr.x());
        assert_eq!(hr.facility(), 0x0);
        assert_eq!(hr.code(), 0x4005);
    }
}