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
//! Helpers for dealing with errno.
//!
//! Most errors in pros-rs are created by reading the last value of ERRNO.
//! This includes the very generic [`PortError`], which is used for most hardware that gets plugged into a port on a V5 Brain.
//!
//! Most of the contents of this file are not public.
/// A result type that makes returning errors easier.
pub type Result<T = ()> = core::result::Result<T, alloc::boxed::Box<dyn core::error::Error>>;
/// Gets the value of errno and sets errno to 0.
pub fn take_errno() -> i32 {
let err = unsafe { *pros_sys::__errno() };
if err != 0 {
unsafe { *pros_sys::__errno() = 0 };
}
err
}
/// Generate an implementation of FromErrno for the given type.
///
/// Example:
/// ```ignore
/// map_errno! {
/// GpsError {
/// EAGAIN => Self::StillCalibrating,
/// }
/// inherit PortError;
/// }
/// ```
#[macro_export]
macro_rules! map_errno {
{
$err_ty:ty { $($errno:pat => $err:expr),*$(,)? }
$(inherit $base:ty;)?
} => {
impl $crate::error::FromErrno for $err_ty {
fn from_errno(num: i32) -> Option<Self> {
#[allow(unused_imports)]
use pros_sys::error::*;
$(
// if the enum we're inheriting from can handle this errno, return it.
if let Some(err) = <$base as $crate::error::FromErrno>::from_errno(num) {
return Some(err.into());
}
)?
match num {
$($errno => Some($err),)*
// this function should only be called if errno is set
0 => panic!("Expected error state in errno, found 0."),
_ => None,
}
}
}
}
}
/// If errno has an error, return early.
#[macro_export]
macro_rules! bail_errno {
() => {{
let errno = $crate::error::take_errno();
if errno != 0 {
let err = $crate::error::FromErrno::from_errno(errno)
.unwrap_or_else(|| panic!("Unknown errno code {errno}"));
return Err(err);
}
}};
}
/// Checks if the value is equal to the error state, and if it is,
/// uses the value of errno to create an error and return early.
#[macro_export]
macro_rules! bail_on {
($err_state:expr, $val:expr) => {{
let val = $val;
#[allow(clippy::cmp_null)]
if val == $err_state {
let errno = $crate::error::take_errno();
let err = $crate::error::FromErrno::from_errno(errno)
.unwrap_or_else(|| panic!("Unknown errno code {errno}"));
return Err(err); // where are we using this in a function that doesn't return result?
}
val
}};
}
use snafu::Snafu;
/// A trait for converting an errno value into an error type.
pub trait FromErrno {
/// Consume the current `errno` and, if it contains a known error, returns Self.
fn from_errno(num: i32) -> Option<Self>
where
Self: Sized;
}
#[derive(Debug, Snafu)]
/// Generic erros that can take place when using ports on the V5 Brain.
pub enum PortError {
/// The specified port is outside of the allowed range!
PortOutOfRange,
/// The specified port couldn't be configured as the specified type.
PortCannotBeConfigured,
/// The specified port is already being used or is mismatched.
AlreadyInUse,
}
map_errno!(PortError {
ENXIO => Self::PortOutOfRange,
ENODEV => Self::PortCannotBeConfigured,
EADDRINUSE => Self::AlreadyInUse,
});