1
+ // Copyright 2018 Developers of the Rand project.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
+ // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
+ // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
+ // option. This file may not be copied, modified, or distributed
7
+ // except according to those terms.
8
+ use core:: { fmt, num:: NonZeroU32 } ;
9
+
10
+ /// A small and `no_std` compatible error type
11
+ ///
12
+ /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
13
+ /// if so, which error code the OS gave the application. If such an error is
14
+ /// encountered, please consult with your system documentation.
15
+ ///
16
+ /// Internally this type is a NonZeroU32, with certain values reserved for
17
+ /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
18
+ ///
19
+ /// *If this crate's `"std"` Cargo feature is enabled*, then:
20
+ /// - [`getrandom::Error`][Error] implements
21
+ /// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
22
+ /// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
23
+ /// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
24
+ #[ derive( Copy , Clone , Eq , PartialEq ) ]
25
+ pub struct Error ( NonZeroU32 ) ;
26
+
27
+ const fn internal_error ( n : u16 ) -> Error {
28
+ // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
29
+ let code = Error :: INTERNAL_START + ( n as u32 ) ;
30
+ Error ( unsafe { NonZeroU32 :: new_unchecked ( code) } )
31
+ }
32
+
33
+ impl Error {
34
+ /// This target/platform is not supported by `getrandom`.
35
+ pub const UNSUPPORTED : Error = internal_error ( 0 ) ;
36
+ /// The platform-specific `errno` returned a non-positive value.
37
+ pub const ERRNO_NOT_POSITIVE : Error = internal_error ( 1 ) ;
38
+ /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
39
+ pub const IOS_SEC_RANDOM : Error = internal_error ( 3 ) ;
40
+ /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
41
+ pub const WINDOWS_RTL_GEN_RANDOM : Error = internal_error ( 4 ) ;
42
+ /// RDRAND instruction failed due to a hardware issue.
43
+ pub const FAILED_RDRAND : Error = internal_error ( 5 ) ;
44
+ /// RDRAND instruction unsupported on this target.
45
+ pub const NO_RDRAND : Error = internal_error ( 6 ) ;
46
+ /// The browser does not have support for `self.crypto`.
47
+ pub const WEB_CRYPTO : Error = internal_error ( 7 ) ;
48
+ /// The browser does not have support for `crypto.getRandomValues`.
49
+ pub const WEB_GET_RANDOM_VALUES : Error = internal_error ( 8 ) ;
50
+ /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
51
+ pub const VXWORKS_RAND_SECURE : Error = internal_error ( 11 ) ;
52
+ /// NodeJS does not have support for the `crypto` module.
53
+ pub const NODE_CRYPTO : Error = internal_error ( 12 ) ;
54
+ /// NodeJS does not have support for `crypto.randomFillSync`.
55
+ pub const NODE_RANDOM_FILL_SYNC : Error = internal_error ( 13 ) ;
56
+
57
+ /// Codes below this point represent OS Errors (i.e. positive i32 values).
58
+ /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
59
+ /// reserved for use by the `rand` and `getrandom` crates.
60
+ pub const INTERNAL_START : u32 = 1 << 31 ;
61
+
62
+ /// Codes at or above this point can be used by users to define their own
63
+ /// custom errors.
64
+ pub const CUSTOM_START : u32 = ( 1 << 31 ) + ( 1 << 30 ) ;
65
+
66
+ /// Extract the raw OS error code (if this error came from the OS)
67
+ ///
68
+ /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
69
+ /// that it works in `no_std` contexts. If this method returns `None`, the
70
+ /// error value can still be formatted via the `Display` implementation.
71
+ ///
72
+ /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
73
+ #[ inline]
74
+ pub fn raw_os_error ( self ) -> Option < i32 > {
75
+ if self . 0 . get ( ) < Self :: INTERNAL_START {
76
+ Some ( self . 0 . get ( ) as i32 )
77
+ } else {
78
+ None
79
+ }
80
+ }
81
+
82
+ /// Extract the bare error code.
83
+ ///
84
+ /// This code can either come from the underlying OS, or be a custom error.
85
+ /// Use [`Error::raw_os_error()`] to disambiguate.
86
+ #[ inline]
87
+ pub const fn code ( self ) -> NonZeroU32 {
88
+ self . 0
89
+ }
90
+ }
91
+
92
+ fn os_err ( _errno : i32 , _buf : & mut [ u8 ] ) -> Option < & str > {
93
+ None
94
+ }
95
+
96
+ impl fmt:: Debug for Error {
97
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
98
+ let mut dbg = f. debug_struct ( "Error" ) ;
99
+ if let Some ( errno) = self . raw_os_error ( ) {
100
+ dbg. field ( "os_error" , & errno) ;
101
+ let mut buf = [ 0u8 ; 128 ] ;
102
+ if let Some ( err) = os_err ( errno, & mut buf) {
103
+ dbg. field ( "description" , & err) ;
104
+ }
105
+ } else if let Some ( desc) = internal_desc ( * self ) {
106
+ dbg. field ( "internal_code" , & self . 0 . get ( ) ) ;
107
+ dbg. field ( "description" , & desc) ;
108
+ } else {
109
+ dbg. field ( "unknown_code" , & self . 0 . get ( ) ) ;
110
+ }
111
+ dbg. finish ( )
112
+ }
113
+ }
114
+
115
+ impl fmt:: Display for Error {
116
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
117
+ if let Some ( errno) = self . raw_os_error ( ) {
118
+ let mut buf = [ 0u8 ; 128 ] ;
119
+ match os_err ( errno, & mut buf) {
120
+ Some ( err) => err. fmt ( f) ,
121
+ None => write ! ( f, "OS Error: {}" , errno) ,
122
+ }
123
+ } else if let Some ( desc) = internal_desc ( * self ) {
124
+ f. write_str ( desc)
125
+ } else {
126
+ write ! ( f, "Unknown Error: {}" , self . 0 . get( ) )
127
+ }
128
+ }
129
+ }
130
+
131
+ impl From < NonZeroU32 > for Error {
132
+ fn from ( code : NonZeroU32 ) -> Self {
133
+ Self ( code)
134
+ }
135
+ }
136
+
137
+ fn internal_desc ( error : Error ) -> Option < & ' static str > {
138
+ match error {
139
+ Error :: UNSUPPORTED => Some ( "getrandom: this target is not supported" ) ,
140
+ Error :: ERRNO_NOT_POSITIVE => Some ( "errno: did not return a positive value" ) ,
141
+ Error :: IOS_SEC_RANDOM => Some ( "SecRandomCopyBytes: iOS Security framework failure" ) ,
142
+ Error :: WINDOWS_RTL_GEN_RANDOM => Some ( "RtlGenRandom: Windows system function failure" ) ,
143
+ Error :: FAILED_RDRAND => Some ( "RDRAND: failed multiple times: CPU issue likely" ) ,
144
+ Error :: NO_RDRAND => Some ( "RDRAND: instruction not supported" ) ,
145
+ Error :: WEB_CRYPTO => Some ( "Web API self.crypto is unavailable" ) ,
146
+ Error :: WEB_GET_RANDOM_VALUES => Some ( "Web API crypto.getRandomValues is unavailable" ) ,
147
+ Error :: VXWORKS_RAND_SECURE => Some ( "randSecure: VxWorks RNG module is not initialized" ) ,
148
+ Error :: NODE_CRYPTO => Some ( "Node.js crypto module is unavailable" ) ,
149
+ Error :: NODE_RANDOM_FILL_SYNC => Some ( "Node.js API crypto.randomFillSync is unavailable" ) ,
150
+ _ => None ,
151
+ }
152
+ }
153
+
154
+ #[ cfg( test) ]
155
+ mod tests {
156
+ use super :: Error ;
157
+ use core:: mem:: size_of;
158
+
159
+ #[ test]
160
+ fn test_size ( ) {
161
+ assert_eq ! ( size_of:: <Error >( ) , 4 ) ;
162
+ assert_eq ! ( size_of:: <Result <( ) , Error >>( ) , 4 ) ;
163
+ }
164
+ }
0 commit comments