pros_core/error.rs
1//! Helpers for dealing with errno.
2//!
3//! Most errors in pros-rs are created by reading the last value of ERRNO.
4//! This includes the very generic [`PortError`], which is used for most hardware that gets plugged into a port on a V5 Brain.
5//!
6//! Most of the contents of this file are not public.
7
8/// A result type that makes returning errors easier.
9pub type Result<T = ()> = core::result::Result<T, alloc::boxed::Box<dyn core::error::Error>>;
10
11/// Gets the value of errno and sets errno to 0.
12pub fn take_errno() -> i32 {
13 let err = unsafe { *pros_sys::__errno() };
14 if err != 0 {
15 unsafe { *pros_sys::__errno() = 0 };
16 }
17 err
18}
19
20/// Generate an implementation of FromErrno for the given type.
21///
22/// Example:
23/// ```ignore
24/// map_errno! {
25/// GpsError {
26/// EAGAIN => Self::StillCalibrating,
27/// }
28/// inherit PortError;
29/// }
30/// ```
31#[macro_export]
32macro_rules! map_errno {
33 {
34 $err_ty:ty { $($errno:pat => $err:expr),*$(,)? }
35 $(inherit $base:ty;)?
36 } => {
37 impl $crate::error::FromErrno for $err_ty {
38 fn from_errno(num: i32) -> Option<Self> {
39 #[allow(unused_imports)]
40 use pros_sys::error::*;
41 $(
42 // if the enum we're inheriting from can handle this errno, return it.
43 if let Some(err) = <$base as $crate::error::FromErrno>::from_errno(num) {
44 return Some(err.into());
45 }
46 )?
47 match num {
48 $($errno => Some($err),)*
49 // this function should only be called if errno is set
50 0 => panic!("Expected error state in errno, found 0."),
51 _ => None,
52 }
53 }
54 }
55 }
56}
57
58/// If errno has an error, return early.
59#[macro_export]
60macro_rules! bail_errno {
61 () => {{
62 let errno = $crate::error::take_errno();
63 if errno != 0 {
64 let err = $crate::error::FromErrno::from_errno(errno)
65 .unwrap_or_else(|| panic!("Unknown errno code {errno}"));
66 return Err(err);
67 }
68 }};
69}
70
71/// Checks if the value is equal to the error state, and if it is,
72/// uses the value of errno to create an error and return early.
73#[macro_export]
74macro_rules! bail_on {
75 ($err_state:expr, $val:expr) => {{
76 let val = $val;
77 #[allow(clippy::cmp_null)]
78 if val == $err_state {
79 let errno = $crate::error::take_errno();
80 let err = $crate::error::FromErrno::from_errno(errno)
81 .unwrap_or_else(|| panic!("Unknown errno code {errno}"));
82 return Err(err); // where are we using this in a function that doesn't return result?
83 }
84 val
85 }};
86}
87use snafu::Snafu;
88
89/// A trait for converting an errno value into an error type.
90pub trait FromErrno {
91 /// Consume the current `errno` and, if it contains a known error, returns Self.
92 fn from_errno(num: i32) -> Option<Self>
93 where
94 Self: Sized;
95}
96
97#[derive(Debug, Snafu)]
98/// Generic erros that can take place when using ports on the V5 Brain.
99pub enum PortError {
100 /// The specified port is outside of the allowed range!
101 PortOutOfRange,
102 /// The specified port couldn't be configured as the specified type.
103 PortCannotBeConfigured,
104 /// The specified port is already being used or is mismatched.
105 AlreadyInUse,
106}
107
108map_errno!(PortError {
109 ENXIO => Self::PortOutOfRange,
110 ENODEV => Self::PortCannotBeConfigured,
111 EADDRINUSE => Self::AlreadyInUse,
112});