This repository has been archived on 2026-08-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
donglora/vendor/semihosting/src/io/error.rs
T
swaitsandClaude Opus 4.6 3102fc747a feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support:
- Splash screen on boot (1.5s) with project name, version, board
- Dashboard showing radio state, config, counters, RSSI/SNR
- RSSI history sparkline (64-sample ring buffer, 2px-wide bars)
- DisplayOn/DisplayOff host commands routed via dedicated channel
- Pure render functions against generic DrawTarget (no hardware coupling)
- Display on/off control bypasses radio task entirely

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:42:54 -06:00

328 lines
11 KiB
Rust

// SPDX-License-Identifier: Apache-2.0 OR MIT
use core::fmt;
use crate::sys;
/// A specialized [`Result`] type for I/O operations.
///
/// See [`std::io::Result` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/io/type.Result.html
pub type Result<T> = core::result::Result<T, Error>;
/// The error type for I/O operations.
///
/// See [`std::io::Error` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/io/struct.Error.html
pub struct Error {
repr: Repr,
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.repr, f)
}
}
macro_rules! const_error {
($kind:expr, $message:expr $(,)?) => {
$crate::io::Error::from_static_message({
const MESSAGE_DATA: $crate::io::error::SimpleMessage =
$crate::io::error::SimpleMessage { kind: $kind, message: $message };
&MESSAGE_DATA
})
};
}
#[allow(dead_code)]
impl Error {
pub(crate) const INVALID_UTF8: Self =
const_error!(ErrorKind::InvalidData, "stream did not contain valid UTF-8");
pub(crate) const READ_EXACT_EOF: Self =
const_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer");
pub(crate) const UNKNOWN_THREAD_COUNT: Self = const_error!(
ErrorKind::NotFound,
"The number of hardware threads is not known for the target platform",
);
pub(crate) const UNSUPPORTED_PLATFORM: Self =
const_error!(ErrorKind::Unsupported, "operation not supported on this platform");
pub(crate) const WRITE_ALL_EOF: Self =
const_error!(ErrorKind::WriteZero, "failed to write whole buffer");
pub(crate) const ZERO_TIMEOUT: Self =
const_error!(ErrorKind::InvalidInput, "cannot set a 0 duration timeout");
#[cfg(feature = "fs")]
pub(crate) const EINVAL: Self = Self { repr: Repr::Os(sys::EINVAL) };
}
#[cfg(feature = "alloc")]
impl From<alloc::ffi::NulError> for Error {
/// Converts a [`alloc::ffi::NulError`] into a [`Error`].
fn from(_: alloc::ffi::NulError) -> Error {
const_error!(ErrorKind::InvalidInput, "data provided contains a nul byte")
}
}
#[cfg(feature = "alloc")]
impl From<alloc::collections::TryReserveError> for Error {
/// Converts `TryReserveError` to an error with [`ErrorKind::OutOfMemory`].
///
/// `TryReserveError` won't be available as the error `source()`,
/// but this may change in the future.
fn from(_: alloc::collections::TryReserveError) -> Error {
// ErrorData::Custom allocates, which isn't great for handling OOM errors.
ErrorKind::OutOfMemory.into()
}
}
enum Repr {
Os(RawOsError),
Simple(ErrorKind),
SimpleMessage(&'static SimpleMessage),
}
/// The type of raw OS error codes returned by [`Error::raw_os_error`].
///
/// See [`std::io::RawOsError` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/nightly/std/io/type.RawOsError.html
pub type RawOsError = i32;
pub(crate) struct SimpleMessage {
pub(crate) kind: ErrorKind,
pub(crate) message: &'static str,
}
/// A list specifying general categories of I/O error.
///
/// See [`std::io::ErrorKind` documentation][std] for details.
///
/// [std]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum ErrorKind {
NotFound,
PermissionDenied,
ConnectionRefused,
ConnectionReset,
HostUnreachable,
NetworkUnreachable,
ConnectionAborted,
NotConnected,
AddrInUse,
AddrNotAvailable,
NetworkDown,
BrokenPipe,
AlreadyExists,
WouldBlock,
NotADirectory,
IsADirectory,
DirectoryNotEmpty,
ReadOnlyFilesystem,
#[doc(hidden)]
__FilesystemLoop, // unstable https://github.com/rust-lang/rust/issues/130188
StaleNetworkFileHandle,
InvalidInput,
InvalidData,
TimedOut,
WriteZero,
StorageFull,
NotSeekable,
QuotaExceeded,
FileTooLarge,
ResourceBusy,
ExecutableFileBusy,
Deadlock,
CrossesDevices,
TooManyLinks,
InvalidFilename,
ArgumentListTooLong,
Interrupted,
Unsupported,
UnexpectedEof,
OutOfMemory,
#[doc(hidden)]
__InProgress, // unstable https://github.com/rust-lang/rust/issues/130840
Other,
// Uncategorized, // unstable, private api
}
impl ErrorKind {
pub(crate) fn as_str(self) -> &'static str {
#[allow(clippy::enum_glob_use)]
use ErrorKind::*;
match self {
AddrInUse => "address in use",
AddrNotAvailable => "address not available",
AlreadyExists => "entity already exists",
ArgumentListTooLong => "argument list too long",
BrokenPipe => "broken pipe",
ConnectionAborted => "connection aborted",
ConnectionRefused => "connection refused",
ConnectionReset => "connection reset",
CrossesDevices => "cross-device link or rename",
Deadlock => "deadlock",
DirectoryNotEmpty => "directory not empty",
ExecutableFileBusy => "executable file busy",
__FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)",
FileTooLarge => "file too large",
HostUnreachable => "host unreachable",
__InProgress => "in progress",
Interrupted => "operation interrupted",
InvalidData => "invalid data",
InvalidFilename => "invalid filename",
InvalidInput => "invalid input parameter",
IsADirectory => "is a directory",
NetworkDown => "network down",
NetworkUnreachable => "network unreachable",
NotADirectory => "not a directory",
NotConnected => "not connected",
NotFound => "entity not found",
NotSeekable => "seek on unseekable file",
Other => "other error",
OutOfMemory => "out of memory",
PermissionDenied => "permission denied",
QuotaExceeded => "quota exceeded",
ReadOnlyFilesystem => "read-only filesystem or storage medium",
ResourceBusy => "resource busy",
StaleNetworkFileHandle => "stale network file handle",
StorageFull => "no storage space",
TimedOut => "timed out",
TooManyLinks => "too many links",
// Uncategorized => "uncategorized error",
UnexpectedEof => "unexpected end of file",
Unsupported => "unsupported",
WouldBlock => "operation would block",
WriteZero => "write zero",
}
}
}
impl fmt::Display for ErrorKind {
/// Shows a human-readable description of the `ErrorKind`.
///
/// This is similar to `impl Display for Error`, but doesn't require first converting to Error.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
/// Intended for use for errors not exposed to the user, where allocating onto
/// the heap (for normal construction via Error::new) is too costly.
impl From<ErrorKind> for Error {
/// Converts an [`ErrorKind`] into an [`Error`].
///
/// This conversion creates a new error with a simple representation of error kind.
#[inline]
fn from(kind: ErrorKind) -> Self {
Self { repr: Repr::Simple(kind) }
}
}
impl Error {
#[inline]
pub(crate) const fn from_static_message(msg: &'static SimpleMessage) -> Error {
Self { repr: Repr::SimpleMessage(msg) }
}
// TODO(io,alloc): provide new,other when alloc feature is enabled?
// TODO(io): last_os_error: Arm semihosting has sys_errno, but MIPS UHI doesn't.
/// Creates a new instance of an `Error` from a particular OS error code.
#[inline]
#[must_use]
pub fn from_raw_os_error(os: RawOsError) -> Self {
Self { repr: Repr::Os(os) }
}
/// Returns the OS error that this error represents (if any).
#[inline]
#[must_use]
pub fn raw_os_error(&self) -> Option<RawOsError> {
match self.repr {
Repr::Os(code) => Some(code),
// Repr::Custom(..) |
Repr::Simple(..) | Repr::SimpleMessage(..) => None,
}
}
/// Returns the corresponding [`ErrorKind`] for this error.
#[inline]
#[must_use]
pub fn kind(&self) -> ErrorKind {
match self.repr {
Repr::Os(code) => sys::decode_error_kind(code),
// Repr::Custom(ref c) => c.kind,
Repr::Simple(kind) => kind,
Repr::SimpleMessage(msg) => msg.kind,
}
}
#[inline]
pub(crate) fn is_interrupted(&self) -> bool {
match self.repr {
Repr::Os(code) => sys::is_interrupted(code),
// Repr::Custom(ref c) => c.kind == ErrorKind::Interrupted,
Repr::Simple(kind) => kind == ErrorKind::Interrupted,
Repr::SimpleMessage(m) => m.kind == ErrorKind::Interrupted,
}
}
}
impl fmt::Debug for Repr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Os(code) => f
.debug_struct("Os")
.field("code", &code)
.field("kind", &sys::decode_error_kind(*code))
// TODO(io)
// .field("message", &sys::os::error_string(code))
.finish(),
// Self::Custom(c) => fmt::Debug::fmt(&c, fmt),
Self::Simple(kind) => f.debug_tuple("Kind").field(&kind).finish(),
Self::SimpleMessage(msg) => f
.debug_struct("Error")
.field("kind", &msg.kind)
.field("message", &msg.message)
.finish(),
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.repr {
Repr::Os(code) => {
// TODO(io)
// let detail = sys::os::error_string(code);
// write!(f, "{detail} (os error {code})")
let detail = sys::decode_error_kind(code);
write!(f, "{detail} (os error {code})")
}
// Repr::Custom(ref c) => c.error.fmt(fmt),
Repr::Simple(kind) => f.write_str(kind.as_str()),
Repr::SimpleMessage(msg) => msg.message.fmt(f),
}
}
}
#[cfg(not(semihosting_no_error_in_core))]
impl core::error::Error for Error {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match self.repr {
Repr::Os(..) | Repr::Simple(..) | Repr::SimpleMessage(..) => None,
// Repr::Custom(c) => c.error.source(),
}
}
}