diff --git a/Cargo.toml b/Cargo.toml index af44fae..79299e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.4", features = ["u embedded-hal = "1" switch-hal = "0.4.0" nb = "0.1.1" -riot-sys = "0.7.13" +riot-sys = "0.7.14" num-traits = { version = "0.2", default-features = false } mutex-trait = "0.2" diff --git a/build.rs b/build.rs index 52b4fde..4bbc8b1 100644 --- a/build.rs +++ b/build.rs @@ -104,6 +104,8 @@ fn main() { "sock_aux_local", "sock_tcp", "sock_udp", + "tiny_strerror", + "tiny_strerror_minimal", "udp", "vfs", "ws281x", diff --git a/src/error.rs b/src/error.rs index f4ea592..65204c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,5 +1,9 @@ //! Common error handling components for the RIOT operating system //! +//! Most fallible operations in the wrappers produce a [NumericError], which is a slightly more +//! precise wrapper around a negative integer. The [NegativeErrorExt::negative_to_error()] trait +//! method can be used to produce such errors when creating wrappers around C functions. +//! //! ## Constants //! //! Several commonly used errors are provided as constants rather than requiring the use of @@ -8,6 +12,8 @@ //! the list). use core::convert::TryInto; +use core::ffi::CStr; +use core::num::NonZero; pub trait NegativeErrorExt { type Out; @@ -25,7 +31,9 @@ pub trait NegativeErrorExt { /// manually implemented newtype around isize that'd be used to represent the Result. #[derive(Debug, PartialEq, Eq)] pub struct NumericError { - number: isize, + // The NonZero doesn't cover the full desired range, but at least Result<(), NumericError> can + // be lean. + number: NonZero, } impl NumericError { @@ -55,12 +63,17 @@ impl NumericError { name > 0, "Error names are expected to be positive for conversion into negative error numbers." ); - NumericError { number: -name } + // Can be an `.unwrap()` once feature(const_trait_impl) is stabilized + let number = match NonZero::new(name) { + Some(n) => n, + _ => panic!("Error names are expected to be positive for conversion into negative error numbers.") + }; + NumericError { number } } /// Numeric value of the error pub const fn number(&self) -> isize { - self.number + self.number.get() } /// Convert the error into an [nb::Error] that is [nb::Error::WouldBlock] if the error is @@ -71,15 +84,33 @@ impl NumericError { } nb::Error::Other(self) } + + fn string(&self) -> Option<&'static CStr> { + #[cfg(all(riot_module_tiny_strerror, not(riot_module_tiny_strerror_minimal)))] + // unsafe: According to C API + // number cast: Disagreements on the numeric error size + // string cast: Disagreements on the signedness of char + return Some(unsafe { + CStr::from_ptr(riot_sys::tiny_strerror(-self.number.get() as _) as _) + }); + + #[cfg(not(all(riot_module_tiny_strerror, not(riot_module_tiny_strerror_minimal))))] + return None; + } +} + +impl core::fmt::Display for NumericError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { + if let Some(s) = self.string() { + write!(f, "Error {} ({})", self.number(), s.to_str().unwrap())?; + } else { + write!(f, "Error {}", self.number())?; + } + Ok(()) + } } -// Would be nice, but there's no strerror -// -// impl core::fmt::Display for NumericError { -// fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> { -// write!(f, "Error {} ({})", self.number(), ...) -// } -// } +impl core::error::Error for NumericError {} impl NegativeErrorExt for T where @@ -91,9 +122,12 @@ where if self >= Self::zero() { Ok(self) } else { - Err(NumericError { - number: self.try_into().unwrap_or(-(riot_sys::EOVERFLOW as isize)), - }) + Err(self + .try_into() + .ok() + .and_then(NonZero::new) + .map(|number| NumericError { number }) + .unwrap_or(EOVERFLOW)) } } }