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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#![cfg(windows)]
//! Windows Error
//!
//! This crate provide simple means to operate WinAPI errors.
//!

use std::os::raw::{
    c_ulong,
    c_void,
    c_ushort,
    c_char
};

type DWORD = c_ulong;

const FORMAT_MESSAGE_ARGUMENT_ARRAY: DWORD = 0x00002000;
const FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
const FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;

extern "system" {
    fn GetLastError() -> DWORD;
    fn FormatMessageW(dwFlags: DWORD,
                      lpSource: *const c_void,
                      dwMessageId: DWORD,
                      dwLanguageId: DWORD,
                      lpBuffer: *mut c_ushort,
                      nSize: DWORD,
                      Arguments: *mut c_char) -> DWORD;
}

fn format_message_error(buff: &[u16]) -> String {
    match unsafe {GetLastError()} {
        122 => String::from_utf16_lossy(buff), //Insufficient memory
        _ => "Unknown Error.".to_string()
    }
}

fn format_message_ok(buff: &[u16]) -> String {
    String::from_utf16_lossy(&buff[0..buff.len()-2])
}

///Returns description of error code.
///
///`Unknown Error.` is returned in case of bad error code.
pub fn format_error(errno: u32) -> String {
    const BUF_SIZE: usize = 512;
    const FMT_FLAGS: DWORD = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY;
    let mut format_buff: [u16; BUF_SIZE] = [0; BUF_SIZE];

    let num_chars: u32 = unsafe { FormatMessageW(FMT_FLAGS,
                                                 std::ptr::null(), errno,
                                                 0, format_buff.as_mut_ptr(),
                                                 BUF_SIZE as u32, std::ptr::null_mut()) };

    if num_chars == 0 {
        format_message_error(&format_buff)
    } else {
        format_message_ok(&format_buff[0..num_chars as usize])
    }
}

use std::error::Error;
use std::fmt;

#[derive(Clone)]
///Represents Windows error code.
pub struct WindowsError(u32);

impl WindowsError {
    ///Constructs new error.
    pub fn new(errno: u32) -> WindowsError {
        WindowsError(errno)
    }

    ///Constructs new error from last happened one via ```GetLastError``` call.
    pub fn from_last_err() -> WindowsError {
        unsafe { WindowsError(GetLastError()) }
    }

    #[inline(always)]
    ///Returns underlying error code.
    pub fn errno(&self) -> u32 {
        self.0
    }

    #[inline(always)]
    ///Returns description of underlying error code.
    pub fn errno_desc(&self) -> String {
        format_error(self.0)
    }
}

//Own debug as derive one is a bit lame
impl fmt::Debug for WindowsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "WinAPI Error({})", self.errno())
    }
}

impl fmt::Display for WindowsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "WinAPI Error({})", self.errno())
    }
}

impl Error for WindowsError {
    fn description(&self) -> &str {
        "WinAPI Error"
    }
}

impl PartialEq for WindowsError {
    fn eq(&self, right: &WindowsError) -> bool {
        self.0 == right.0
    }

    fn ne(&self, right: &WindowsError) -> bool {
        self.0 != right.0
    }
}

macro_rules! impl_traits
{
    ($($t:ty), +) => {
        $(
            impl From<$t> for WindowsError {
                fn from(num: $t) -> Self {
                    WindowsError(num as u32)
                }
            }

            impl Into<$t> for WindowsError {
                fn into(self) -> $t {
                    self.0 as $t
                }
            }

            impl PartialEq<$t> for WindowsError {
                fn eq(&self, right: &$t) -> bool {
                    self.0 == *right as u32
                }

                fn ne(&self, right: &$t) -> bool {
                    self.0 != *right as u32
                }
            }
        )+
    };
}

impl_traits!(u32, u16, u8, usize, i32, i16, i8, isize);