1#![no_std]
26
27use core::fmt;
28use core::result;
29use core::str;
30
31pub type Result<T> = result::Result<T, Error>;
32
33#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
48#[repr(transparent)]
49pub struct Error {
50 pub errno: i32,
51}
52
53impl Error {
54 pub const fn new(errno: i32) -> Self {
55 Self { errno }
56 }
57 pub fn last() -> Self {
58 Self { errno: errno() }
59 }
60
61 pub const fn inval() -> Self {
63 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
64 {
65 Self { errno: EINVAL }
66 }
67 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
68 {
69 Self {
70 errno: WSA_INVALID_PARAMETER,
71 }
72 }
73 }
74
75 pub const fn noent() -> Self {
77 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
78 {
79 Self { errno: ENOENT }
80 }
81 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
82 {
83 Self { errno: 1 }
85 }
86 }
87
88 pub const fn perm() -> Self {
90 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
91 {
92 Self { errno: EPERM }
93 }
94 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
95 {
96 Self { errno: 5 }
98 }
99 }
100
101 pub const fn nomem() -> Self {
103 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
104 {
105 Self { errno: ENOMEM }
106 }
107 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
108 {
109 Self {
110 errno: WSA_NOT_ENOUGH_MEMORY,
111 }
112 }
113 }
114
115 pub const fn busy() -> Self {
117 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
118 {
119 Self { errno: EBUSY }
120 }
121 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
122 {
123 Self { errno: 170 }
125 }
126 }
127
128 pub const fn timedout() -> Self {
130 #[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
131 {
132 Self { errno: ETIMEDOUT }
133 }
134 #[cfg(all(target_os = "windows", not(target_env = "gnu")))]
135 {
136 Self {
137 errno: WSAETIMEDOUT,
138 }
139 }
140 }
141
142 pub const fn general() -> Self {
144 Self { errno: -1 }
145 }
146}
147
148impl Default for Error {
149 fn default() -> Self {
150 Error::general()
151 }
152}
153
154impl From<i32> for Error {
155 fn from(i32: i32) -> Self {
156 Self::new(i32)
157 }
158}
159
160impl From<Error> for Result<()> {
161 fn from(err: Error) -> Self {
162 if err.errno == 0 {
163 Ok(())
164 } else {
165 Err(err)
166 }
167 }
168}
169
170const ERRMSG_MAX_SIZE: usize = 64;
171impl fmt::Display for Error {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
173 let mut msg = [0_u8; ERRMSG_MAX_SIZE];
174 write!(f, "{}: {}", self.errno, errmsg(self.errno, &mut msg[..]))
175 }
176}
177
178impl fmt::Debug for Error {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
180 let mut msg = [0_u8; ERRMSG_MAX_SIZE];
181 write!(f, "{}: {}", self.errno, errmsg(self.errno, &mut msg[..]))
182 }
183}
184
185#[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
186fn format_message_utf8<'a>(input: &[u8], output: &'a mut [u8]) -> &'a str {
187 let Ok(s) = str::from_utf8(input) else {
188 return get_message(output, 0);
189 };
190 if input.len() <= output.len() {
191 output[..input.len()].copy_from_slice(input);
192 return str::from_utf8(&output[..input.len()]).unwrap();
193 }
194 return truncate_message(s, output);
195}
196
197#[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
198fn truncate_message<'a>(input: &str, output: &'a mut [u8]) -> &'a str {
199 let mut pos = 0;
200 for c in input.chars() {
201 if !put_message(output, &mut pos, c) {
202 break;
203 }
204 }
205 get_message(output, pos)
206}
207
208fn put_message(output: &mut [u8], pos: &mut usize, c: char) -> bool {
209 if *pos + c.len_utf8() <= output.len() {
210 c.encode_utf8(&mut output[*pos..]);
211 *pos += c.len_utf8();
212 return true;
213 }
214 false
215}
216
217fn get_message(output: &[u8], len: usize) -> &str {
218 if len > 0 {
219 str::from_utf8(&output[..len]).unwrap()
220 } else {
221 "Unknown error"
222 }
223}
224
225#[cfg(all(target_os = "windows", not(target_env = "gnu")))]
226fn format_message_utf16<'a>(input: &[u16], output: &'a mut [u8]) -> &'a str {
227 let mut pos = 0;
228 char::decode_utf16(input.iter().copied()).any(|c| {
229 let Ok(c) = c else {
230 pos = 0;
231 return true;
232 };
233 !put_message(output, &mut pos, c)
234 });
235 get_message(output, pos)
236}
237
238#[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
239mod unix;
240#[cfg(any(unix, all(target_os = "windows", target_env = "gnu")))]
241pub use unix::*;
242
243#[cfg(all(target_os = "windows", not(target_env = "gnu")))]
244mod windows;
245#[cfg(all(target_os = "windows", not(target_env = "gnu")))]
246pub use windows::*;
247
248#[cfg(test)]
249mod test {
250 use super::*;
251 extern crate std;
252 use std::println;
253
254 #[test]
255 fn test_errno() {
256 set_errno(100);
257 assert_eq!(errno(), 100);
258 }
259
260 #[test]
261 fn test_print() {
262 for n in 0..=102 {
263 let err = Error::new(n);
264 println!("[{}]\n[{:?}]\n", err, err);
265 }
266
267 set_errno(4);
268 println!("5: {}", errmsg(5, &mut [0_u8; 7]));
269 }
270}