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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! **Crate features**
//!
//! * `"std"`
//! Enabled by default. Disable to make the library `#![no_std]`.

#![deny(warnings)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
#![allow(clippy::unnecessary_cast)]

#![cfg_attr(not(feature="std"), no_std)]
#[cfg(feature="std")]
extern crate core;

#[cfg(test)]
extern crate std;

#[cfg(all(not(windows), not(custom_errno)))]
mod posix;
#[cfg(all(not(windows), not(custom_errno)))]
use posix::*;

#[cfg(all(windows, not(custom_errno)))]
mod winapi;
#[cfg(all(windows, not(custom_errno)))]
use crate::winapi::*;

#[cfg(custom_errno)]
mod custom;
#[cfg(custom_errno)]
use custom::*;

use core::fmt::{self, Formatter};
#[cfg(feature="std")]
use std::error::Error;
#[cfg(feature="std")]
use std::io::{self};

/// Wraps a platform-specific error code.
#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
pub struct Errno(pub i32);

impl fmt::Display for Errno {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        errno_fmt(self.0, f)
    }
}

#[cfg(feature="std")]
impl Error for Errno { }

#[cfg(feature="std")]
impl From<Errno> for io::Error {
    fn from(e: Errno) -> Self {
        io::Error::from_raw_os_error(e.0)
    }
}

/// Returns the platform-specific value of `errno`.
pub fn errno() -> Errno { Errno(errno_raw()) }

/// Sets the platform-specific value of `errno`.
pub fn set_errno(err: Errno) { set_errno_raw(err.0) }

#[cfg(test)]
mod test {
    use crate::*;
    use copy_from_str::CopyFromStrExt;
    use core::fmt::{Write};
    use core::str::{self};
    use quickcheck_macros::quickcheck;

    #[cfg(custom_errno)]
    mod custom_errno {
        use std::cell::Cell;
        use std::fmt::{self, Formatter};
        use std::thread_local;

        thread_local! {
            static ERRNO: Cell<i32> = Cell::new(0);
        }

        #[no_mangle]
        extern "Rust" fn rust_errno() -> i32 { ERRNO.with(|x| x.get()) }

        #[no_mangle]
        extern "Rust" fn rust_set_errno(e: i32) {
            ERRNO.with(|x| x.set(e))
        }

        #[no_mangle]
        extern "Rust" fn rust_errno_fmt(e: i32, f: &mut Formatter) -> fmt::Result {
            write!(f, "Error {}", e)
        }
    }

    struct Buf<'a> {
        s: &'a mut str,
        len: usize,
    }

    impl<'a> Write for Buf<'a> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            let advanced_len = self.len.checked_add(s.len()).unwrap();
            self.s[self.len .. advanced_len].copy_from_str(s);
            self.len = advanced_len;
            Ok(())
        }
    }

    #[quickcheck]
    fn errno_after_set_errno(e: i32) -> bool {
        set_errno(Errno(e));
        errno() == Errno(e)
    }

    #[quickcheck]
    fn error_display(e: i32) -> bool {
        let mut buf = [0; 1024];
        let buf = str::from_utf8_mut(&mut buf[..]).unwrap();
        let mut buf = Buf { s: buf, len: 0 };
        write!(&mut buf, "{}", Errno(e)).unwrap();
        let res = &buf.s[.. buf.len];
        if res.len() <= 5 { return false; }
        let end = res.chars().last().unwrap();
        end.is_ascii_alphanumeric() && !end.is_whitespace() || end == '.'
    }
}

#[cfg(all(test, not(windows), not(target_os="macos"), not(custom_errno)))]
mod test_localization {
    use crate::*;
    use copy_from_str::CopyFromStrExt;
    use core::fmt::{Write};
    use core::str::{self};
    use libc::{LC_ALL, EACCES, setlocale};

    struct Buf<'a> {
        s: &'a mut str,
        len: usize,
    }

    impl<'a> Write for Buf<'a> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            let advanced_len = self.len.checked_add(s.len()).unwrap();
            self.s[self.len .. advanced_len].copy_from_str(s);
            self.len = advanced_len;
            Ok(())
        }
    }

    struct DefaultLocale;

    impl Drop for DefaultLocale {
        fn drop(&mut self) {
            unsafe { setlocale(LC_ALL, b"\0".as_ptr() as *const _); }
        }
    }

    #[test]
    fn localized_messages() {
        let _default_locale = DefaultLocale;
        let locales: &[&'static [u8]] = &[
            b"en_US.UTF-8\0",
            b"ja_JP.EUC-JP\0",
            b"uk_UA.KOI8-U\0",
            b"uk_UA.UTF-8\0"
        ];
        for &locale in locales {
            unsafe { setlocale(LC_ALL, locale.as_ptr() as *const _) };
            let msg = match locale.split(|&b| b == b'.').next().unwrap() {
                b"en_US" => "Permission denied",
                b"ja_JP" => "許可がありません",
                b"uk_UA" => "Відмовлено у доступі",
                _ => panic!("message?"),
            };
            let mut buf = [0; 1024];
            let buf = str::from_utf8_mut(&mut buf[..]).unwrap();
            let mut buf = Buf { s: buf, len: 0 };
            write!(&mut buf, "{}", Errno(EACCES)).unwrap();
            let res = &buf.s[.. buf.len];
            assert_eq!(res, msg);
        }
    }
}