freebsd_errno/
freebsd-errno.rs

1// Copyright (c) 2022 John Millikin <john@john-millikin.com>
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted.
5//
6// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
8// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
10// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
11// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
12// PERFORMANCE OF THIS SOFTWARE.
13//
14// SPDX-License-Identifier: 0BSD
15
16//! This library defines an [Error] struct that represents error numbers
17//! returned from FreeBSD system calls.
18
19#![no_std]
20
21use core::{fmt, num};
22use core::convert::TryFrom;
23
24/// Type for error numbers returned from FreeBSD system calls.
25///
26/// The `Error` type implements `PartialEq` for many integer types, and
27/// (optionally) with the POSIX error numbers defined in the [`posix-errno`]
28/// library.
29///
30/// [`posix-errno`]: https://crates.io/crates/posix-errno
31#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
32pub struct Error(num::NonZeroI32);
33
34impl Error {
35	/// Create a new error from a raw error number. If `errno` is zero,
36	/// returns `None`.
37	pub const fn new(errno: i32) -> Option<Error> {
38		match num::NonZeroI32::new(errno) {
39			Some(n) => Some(Self(n)),
40			None => errno_out_of_range(),
41		}
42	}
43
44	/// Unsafely create a new error from a raw error number.
45	///
46	/// # Safety
47	///
48	/// The caller must ensure that `errno` is non-zero.
49	#[inline]
50	pub const unsafe fn new_unchecked(errno: i32) -> Error {
51		Error(num::NonZeroI32::new_unchecked(errno))
52	}
53
54	/// Returns the error number as a primitive `i32`.
55	#[inline]
56	pub const fn get(&self) -> i32 {
57		self.0.get()
58	}
59
60	/// Returns the error number as a [`NonZeroI32`](num::NonZeroI32).
61	#[inline]
62	pub const fn get_nonzero(&self) -> num::NonZeroI32 {
63		self.0
64	}
65}
66
67#[cold]
68#[inline]
69const fn errno_out_of_range() -> Option<Error> {
70	None
71}
72
73impl From<Error> for i32 {
74	#[inline]
75	fn from(err: Error) -> i32 {
76		err.0.get()
77	}
78}
79
80impl From<Error> for num::NonZeroI32 {
81	#[inline]
82	fn from(err: Error) -> num::NonZeroI32 {
83		err.0
84	}
85}
86
87impl From<Error> for i64 {
88	#[inline]
89	fn from(err: Error) -> i64 {
90		err.0.get().into()
91	}
92}
93
94impl From<Error> for num::NonZeroI64 {
95	#[inline]
96	fn from(err: Error) -> num::NonZeroI64 {
97		err.0.into()
98	}
99}
100
101impl fmt::Binary for Error {
102	#[inline]
103	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
104		self.0.fmt(fmt)
105	}
106}
107
108impl fmt::LowerHex for Error {
109	#[inline]
110	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
111		self.0.fmt(fmt)
112	}
113}
114
115impl fmt::UpperHex for Error {
116	#[inline]
117	fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
118		self.0.fmt(fmt)
119	}
120}
121
122impl PartialEq<u64> for Error {
123	#[inline]
124	fn eq(&self, other: &u64) -> bool {
125		u64::try_from(self.0.get()).map_or(false, |x| x == *other)
126	}
127}
128
129impl PartialEq<Error> for u64 {
130	#[inline]
131	fn eq(&self, other: &Error) -> bool {
132		u64::try_from(other.0.get()).map_or(false, |x| x == *self)
133	}
134}
135
136impl PartialEq<usize> for Error {
137	#[inline]
138	fn eq(&self, other: &usize) -> bool {
139		usize::try_from(self.0.get()).map_or(false, |x| x == *other)
140	}
141}
142
143impl PartialEq<Error> for usize {
144	#[inline]
145	fn eq(&self, other: &Error) -> bool {
146		usize::try_from(other.0.get()).map_or(false, |x| x == *self)
147	}
148}
149
150impl PartialEq<isize> for Error {
151	#[inline]
152	fn eq(&self, other: &isize) -> bool {
153		isize::try_from(self.0.get()).map_or(false, |x| x == *other)
154	}
155}
156
157impl PartialEq<Error> for isize {
158	#[inline]
159	fn eq(&self, other: &Error) -> bool {
160		isize::try_from(other.0.get()).map_or(false, |x| x == *self)
161	}
162}
163
164impl PartialEq<num::NonZeroU64> for Error {
165	#[inline]
166	fn eq(&self, other: &num::NonZeroU64) -> bool {
167		u64::try_from(self.0.get()).map_or(false, |x| x == other.get())
168	}
169}
170
171impl PartialEq<Error> for num::NonZeroU64 {
172	#[inline]
173	fn eq(&self, other: &Error) -> bool {
174		u64::try_from(other.0.get()).map_or(false, |x| x == self.get())
175	}
176}
177
178impl PartialEq<num::NonZeroUsize> for Error {
179	#[inline]
180	fn eq(&self, other: &num::NonZeroUsize) -> bool {
181		usize::try_from(self.0.get()).map_or(false, |x| x == other.get())
182	}
183}
184
185impl PartialEq<Error> for num::NonZeroUsize {
186	#[inline]
187	fn eq(&self, other: &Error) -> bool {
188		usize::try_from(other.0.get()).map_or(false, |x| x == self.get())
189	}
190}
191
192impl PartialEq<num::NonZeroIsize> for Error {
193	#[inline]
194	fn eq(&self, other: &num::NonZeroIsize) -> bool {
195		isize::try_from(self.0.get()).map_or(false, |x| x == other.get())
196	}
197}
198
199impl PartialEq<Error> for num::NonZeroIsize {
200	#[inline]
201	fn eq(&self, other: &Error) -> bool {
202		isize::try_from(other.0.get()).map_or(false, |x| x == self.get())
203	}
204}
205
206macro_rules! impl_partial_eq {
207	($t:ty, $via_t:ty) => {
208		impl PartialEq<$t> for Error {
209			#[inline]
210			fn eq(&self, other: &$t) -> bool {
211				<$via_t>::from(self.0.get()) == <$via_t>::from(*other)
212			}
213		}
214
215		impl PartialEq<Error> for $t {
216			#[inline]
217			fn eq(&self, other: &Error) -> bool {
218				<$via_t>::from(other.0.get()) == <$via_t>::from(*self)
219			}
220		}
221	};
222}
223
224macro_rules! impl_partial_eq_nonzero {
225	($t:ty, $via_t:ty) => {
226		impl PartialEq<$t> for Error {
227			#[inline]
228			fn eq(&self, other: &$t) -> bool {
229				<$via_t>::from(self.0.get()) == <$via_t>::from(other.get())
230			}
231		}
232
233		impl PartialEq<Error> for $t {
234			#[inline]
235			fn eq(&self, other: &Error) -> bool {
236				<$via_t>::from(other.0.get()) == <$via_t>::from(self.get())
237			}
238		}
239	};
240}
241
242impl_partial_eq!(i16, i32);
243impl_partial_eq!(i32, i32);
244impl_partial_eq!(i64, i64);
245impl_partial_eq!(u16, i32);
246impl_partial_eq!(u32, i64);
247
248impl_partial_eq_nonzero!(num::NonZeroI16, i32);
249impl_partial_eq_nonzero!(num::NonZeroI32, i32);
250impl_partial_eq_nonzero!(num::NonZeroI64, i64);
251impl_partial_eq_nonzero!(num::NonZeroU16, i32);
252impl_partial_eq_nonzero!(num::NonZeroU32, i64);
253
254macro_rules! errno_constants {
255	( $( $(#[$meta:meta])* $name:ident = $value:literal , )+ ) => {
256		$(
257			$(#[$meta])*
258			pub const $name: $crate::Error = unsafe {
259				$crate::Error::new_unchecked($value)
260			};
261		)*
262
263		#[inline]
264		const fn err_name(err: $crate::Error) -> Option<&'static str> {
265			match err.0.get() {
266			$(
267				$value => Some(stringify!($name)),
268			)*
269				_ => None,
270			}
271		}
272	}
273}
274
275impl fmt::Debug for Error {
276	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277		match err_name(*self) {
278			Some(name) => f.write_str(name),
279			_ => f.debug_tuple("Error").field(&self.0.get()).finish(),
280		}
281	}
282}
283
284#[cfg(feature = "posix-traits")]
285const fn from_posix(err: posix_errno::Error) -> Option<Error> {
286	use posix_errno::Error as P;
287	match err {
288		P::E2BIG           => Some(E2BIG),
289		P::EACCES          => Some(EACCES),
290		P::EADDRINUSE      => Some(EADDRINUSE),
291		P::EADDRNOTAVAIL   => Some(EADDRNOTAVAIL),
292		P::EAFNOSUPPORT    => Some(EAFNOSUPPORT),
293		P::EAGAIN          => Some(EAGAIN),
294		P::EALREADY        => Some(EALREADY),
295		P::EBADF           => Some(EBADF),
296		P::EBADMSG         => Some(EBADMSG),
297		P::EBUSY           => Some(EBUSY),
298		P::ECANCELED       => Some(ECANCELED),
299		P::ECHILD          => Some(ECHILD),
300		P::ECONNABORTED    => Some(ECONNABORTED),
301		P::ECONNREFUSED    => Some(ECONNREFUSED),
302		P::ECONNRESET      => Some(ECONNRESET),
303		P::EDEADLK         => Some(EDEADLK),
304		P::EDESTADDRREQ    => Some(EDESTADDRREQ),
305		P::EDOM            => Some(EDOM),
306		P::EDQUOT          => Some(EDQUOT),
307		P::EEXIST          => Some(EEXIST),
308		P::EFAULT          => Some(EFAULT),
309		P::EFBIG           => Some(EFBIG),
310		P::EHOSTUNREACH    => Some(EHOSTUNREACH),
311		P::EIDRM           => Some(EIDRM),
312		P::EILSEQ          => Some(EILSEQ),
313		P::EINPROGRESS     => Some(EINPROGRESS),
314		P::EINTR           => Some(EINTR),
315		P::EINVAL          => Some(EINVAL),
316		P::EIO             => Some(EIO),
317		P::EISCONN         => Some(EISCONN),
318		P::EISDIR          => Some(EISDIR),
319		P::ELOOP           => Some(ELOOP),
320		P::EMFILE          => Some(EMFILE),
321		P::EMLINK          => Some(EMLINK),
322		P::EMSGSIZE        => Some(EMSGSIZE),
323		P::EMULTIHOP       => Some(EMULTIHOP),
324		P::ENAMETOOLONG    => Some(ENAMETOOLONG),
325		P::ENETDOWN        => Some(ENETDOWN),
326		P::ENETRESET       => Some(ENETRESET),
327		P::ENETUNREACH     => Some(ENETUNREACH),
328		P::ENFILE          => Some(ENFILE),
329		P::ENOBUFS         => Some(ENOBUFS),
330		P::ENODATA         => None,
331		P::ENODEV          => Some(ENODEV),
332		P::ENOENT          => Some(ENOENT),
333		P::ENOEXEC         => Some(ENOEXEC),
334		P::ENOLCK          => Some(ENOLCK),
335		P::ENOLINK         => Some(ENOLINK),
336		P::ENOMEM          => Some(ENOMEM),
337		P::ENOMSG          => Some(ENOMSG),
338		P::ENOPROTOOPT     => Some(ENOPROTOOPT),
339		P::ENOSPC          => Some(ENOSPC),
340		P::ENOSR           => None,
341		P::ENOSTR          => None,
342		P::ENOSYS          => Some(ENOSYS),
343		P::ENOTCONN        => Some(ENOTCONN),
344		P::ENOTDIR         => Some(ENOTDIR),
345		P::ENOTEMPTY       => Some(ENOTEMPTY),
346		P::ENOTRECOVERABLE => Some(ENOTRECOVERABLE),
347		P::ENOTSOCK        => Some(ENOTSOCK),
348		P::ENOTSUP         => Some(ENOTSUP),
349		P::ENOTTY          => Some(ENOTTY),
350		P::ENXIO           => Some(ENXIO),
351		P::EOPNOTSUPP      => Some(EOPNOTSUPP),
352		P::EOVERFLOW       => Some(EOVERFLOW),
353		P::EOWNERDEAD      => Some(EOWNERDEAD),
354		P::EPERM           => Some(EPERM),
355		P::EPIPE           => Some(EPIPE),
356		P::EPROTO          => Some(EPROTO),
357		P::EPROTONOSUPPORT => Some(EPROTONOSUPPORT),
358		P::EPROTOTYPE      => Some(EPROTOTYPE),
359		P::ERANGE          => Some(ERANGE),
360		P::EROFS           => Some(EROFS),
361		P::ESPIPE          => Some(ESPIPE),
362		P::ESRCH           => Some(ESRCH),
363		P::ESTALE          => Some(ESTALE),
364		P::ETIME           => None,
365		P::ETIMEDOUT       => Some(ETIMEDOUT),
366		P::ETXTBSY         => Some(ETXTBSY),
367		P::EWOULDBLOCK     => Some(EWOULDBLOCK),
368		P::EXDEV           => Some(EXDEV),
369		_ => None,
370	}
371}
372
373#[cfg(any(feature = "posix-traits", doc))]
374impl PartialEq<posix_errno::Error> for Error {
375	#[inline]
376	fn eq(&self, other: &posix_errno::Error) -> bool {
377		from_posix(*other) == Some(*self)
378	}
379}
380
381#[cfg(any(feature = "posix-traits", doc))]
382impl PartialEq<Error> for posix_errno::Error {
383	#[inline]
384	fn eq(&self, other: &Error) -> bool {
385		from_posix(*self) == Some(*other)
386	}
387}
388
389errno_constants! {
390	// https://cgit.freebsd.org/src/tree/sys/sys/errno.h?h=release/13.1.0
391
392	/// Operation not permitted
393	EPERM = 1,
394	/// No such file or directory
395	ENOENT = 2,
396	/// No such process
397	ESRCH = 3,
398	/// Interrupted system call
399	EINTR = 4,
400	/// Input/output error
401	EIO = 5,
402	/// Device not configured
403	ENXIO = 6,
404	/// Argument list too long
405	E2BIG = 7,
406	/// Exec format error
407	ENOEXEC = 8,
408	/// Bad file descriptor
409	EBADF = 9,
410	/// No child processes
411	ECHILD = 10,
412	/// Resource deadlock avoided
413	EDEADLK = 11,
414	/// Cannot allocate memory
415	ENOMEM = 12,
416	/// Permission denied
417	EACCES = 13,
418	/// Bad address
419	EFAULT = 14,
420	/// Block device required
421	ENOTBLK = 15,
422	/// Device busy
423	EBUSY = 16,
424	/// File exists
425	EEXIST = 17,
426	/// Cross-device link
427	EXDEV = 18,
428	/// Operation not supported by device
429	ENODEV = 19,
430	/// Not a directory
431	ENOTDIR = 20,
432	/// Is a directory
433	EISDIR = 21,
434	/// Invalid argument
435	EINVAL = 22,
436	/// Too many open files in system
437	ENFILE = 23,
438	/// Too many open files
439	EMFILE = 24,
440	/// Inappropriate ioctl for device
441	ENOTTY = 25,
442	/// Text file busy
443	ETXTBSY = 26,
444	/// File too large
445	EFBIG = 27,
446	/// No space left on device
447	ENOSPC = 28,
448	/// Illegal seek
449	ESPIPE = 29,
450	/// Read-only filesystem
451	EROFS = 30,
452	/// Too many links
453	EMLINK = 31,
454	/// Broken pipe
455	EPIPE = 32,
456	/// Numerical argument out of domain
457	EDOM = 33,
458	/// Result too large
459	ERANGE = 34,
460	/// Resource temporarily unavailable
461	EAGAIN = 35,
462	/// Operation now in progress
463	EINPROGRESS = 36,
464	/// Operation already in progress
465	EALREADY = 37,
466	/// Socket operation on non-socket
467	ENOTSOCK = 38,
468	/// Destination address required
469	EDESTADDRREQ = 39,
470	/// Message too long
471	EMSGSIZE = 40,
472	/// Protocol wrong type for socket
473	EPROTOTYPE = 41,
474	/// Protocol not available
475	ENOPROTOOPT = 42,
476	/// Protocol not supported
477	EPROTONOSUPPORT = 43,
478	/// Socket type not supported
479	ESOCKTNOSUPPORT = 44,
480	/// Operation not supported
481	EOPNOTSUPP = 45,
482	/// Protocol family not supported
483	EPFNOSUPPORT = 46,
484	/// Address family not supported by protocol family
485	EAFNOSUPPORT = 47,
486	/// Address already in use
487	EADDRINUSE = 48,
488	/// Can't assign requested address
489	EADDRNOTAVAIL = 49,
490	/// Network is down
491	ENETDOWN = 50,
492	/// Network is unreachable
493	ENETUNREACH = 51,
494	/// Network dropped connection on reset
495	ENETRESET = 52,
496	/// Software caused connection abort
497	ECONNABORTED = 53,
498	/// Connection reset by peer
499	ECONNRESET = 54,
500	/// No buffer space available
501	ENOBUFS = 55,
502	/// Socket is already connected
503	EISCONN = 56,
504	/// Socket is not connected
505	ENOTCONN = 57,
506	/// Can't send after socket shutdown
507	ESHUTDOWN = 58,
508	/// Too many references: can't splice
509	ETOOMANYREFS = 59,
510	/// Operation timed out
511	ETIMEDOUT = 60,
512	/// Connection refused
513	ECONNREFUSED = 61,
514	/// Too many levels of symbolic links
515	ELOOP = 62,
516	/// File name too long
517	ENAMETOOLONG = 63,
518	/// Host is down
519	EHOSTDOWN = 64,
520	/// No route to host
521	EHOSTUNREACH = 65,
522	/// Directory not empty
523	ENOTEMPTY = 66,
524	/// Too many processes
525	EPROCLIM = 67,
526	/// Too many users
527	EUSERS = 68,
528	/// Disc quota exceeded
529	EDQUOT = 69,
530	/// Stale NFS file handle
531	ESTALE = 70,
532	/// Too many levels of remote in path
533	EREMOTE = 71,
534	/// RPC struct is bad
535	EBADRPC = 72,
536	/// RPC version wrong
537	ERPCMISMATCH = 73,
538	/// RPC prog. not avail
539	EPROGUNAVAIL = 74,
540	/// Program version wrong
541	EPROGMISMATCH = 75,
542	/// Bad procedure for program
543	EPROCUNAVAIL = 76,
544	/// No locks available
545	ENOLCK = 77,
546	/// Function not implemented
547	ENOSYS = 78,
548	/// Inappropriate file type or format
549	EFTYPE = 79,
550	/// Authentication error
551	EAUTH = 80,
552	/// Need authenticator
553	ENEEDAUTH = 81,
554	/// Identifier removed
555	EIDRM = 82,
556	/// No message of desired type
557	ENOMSG = 83,
558	/// Value too large to be stored in data type
559	EOVERFLOW = 84,
560	/// Operation canceled
561	ECANCELED = 85,
562	/// Illegal byte sequence
563	EILSEQ = 86,
564	/// Attribute not found
565	ENOATTR = 87,
566	/// Programming error
567	EDOOFUS = 88,
568	/// Bad message
569	EBADMSG = 89,
570	/// Multihop attempted
571	EMULTIHOP = 90,
572	/// Link has been severed
573	ENOLINK = 91,
574	/// Protocol error
575	EPROTO = 92,
576	/// Capabilities insufficient
577	ENOTCAPABLE = 93,
578	/// Not permitted in capability mode
579	ECAPMODE = 94,
580	/// State not recoverable
581	ENOTRECOVERABLE = 95,
582	/// Previous owner died
583	EOWNERDEAD = 96,
584	/// Integrity check failed
585	EINTEGRITY = 97,
586}
587
588/// Operation not supported (alias for [EOPNOTSUPP])
589pub const ENOTSUP: crate::Error = EOPNOTSUPP;
590
591/// Operation would block (alias for [EAGAIN])
592pub const EWOULDBLOCK: crate::Error = EAGAIN;