Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions embedded-io-async/src/impls/slice_mut.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use core::mem;
use embedded_io::SliceWriteError;
use embedded_io::ErrorKind;

use crate::Write;

Expand All @@ -17,7 +17,7 @@ impl Write for &mut [u8] {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let amt = core::cmp::min(buf.len(), self.len());
if !buf.is_empty() && amt == 0 {
return Err(SliceWriteError::Full);
return Err(ErrorKind::StorageFull);
}
let (a, b) = mem::take(self).split_at_mut(amt);
a.copy_from_slice(&buf[..amt]);
Expand Down
32 changes: 26 additions & 6 deletions embedded-io-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ extern crate alloc;

mod impls;

pub use embedded_io::{
Error, ErrorKind, ErrorType, ReadExactError, ReadReady, SeekFrom, WriteReady,
};
pub use embedded_io::{Error, ErrorKind, ErrorType, ReadReady, SeekFrom, WriteReady};

/// Async reader.
///
Expand Down Expand Up @@ -54,6 +52,15 @@ pub trait Read: ErrorType {
/// the hardware with e.g. DMA.
///
/// Implementations should document whether they're actually side-effect-free on cancel or not.
///
/// # Errors
///
/// If this function encounters any form of I/O or other error, an error
/// variant will be returned. If an error is returned then it must be
/// guaranteed that no bytes were read.
///
/// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
/// operation should be retried if there is nothing else to do.
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;

/// Read the exact number of bytes required to fill `buf`.
Expand All @@ -63,18 +70,31 @@ pub trait Read: ErrorType {
///
/// This function is not side-effect-free on cancel (AKA "cancel-safe"), i.e. if you cancel (drop) a returned
/// future that hasn't completed yet, some bytes might have already been read, which will get lost.
async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
////
/// /// # Errors
///
/// If this function encounters an "end of file" before completely filling
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
/// The contents of `buf` are unspecified in this case.
///
/// If any other read error is encountered then this function immediately
/// returns. The contents of `buf` are unspecified in this case.
///
/// If this function returns an error, it is unspecified how many bytes it
/// has read, but it will never read more than would be necessary to
/// completely fill the buffer.
async fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
while !buf.is_empty() {
match self.read(buf).await {
Ok(0) => break,
Ok(n) => buf = &mut buf[n..],
Err(e) => return Err(ReadExactError::Other(e)),
Err(e) => return Err(e),
}
}
if buf.is_empty() {
Ok(())
} else {
Err(ReadExactError::UnexpectedEof)
Err(ErrorKind::UnexpectedEof.into())
}
}
}
Expand Down
14 changes: 3 additions & 11 deletions embedded-io/src/impls/slice_mut.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
use crate::{Error, ErrorKind, ErrorType, SliceWriteError, Write, WriteReady};
use crate::{ErrorKind, ErrorType, SliceWriteError, Write, WriteReady};
use core::mem;

impl Error for SliceWriteError {
fn kind(&self) -> ErrorKind {
match self {
SliceWriteError::Full => ErrorKind::WriteZero,
}
}
}

impl ErrorType for &mut [u8] {
type Error = SliceWriteError;
type Error = ErrorKind;
}

impl core::fmt::Display for SliceWriteError {
Expand All @@ -34,7 +26,7 @@ impl Write for &mut [u8] {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
let amt = core::cmp::min(buf.len(), self.len());
if !buf.is_empty() && amt == 0 {
return Err(SliceWriteError::Full);
return Err(ErrorKind::StorageFull);
}
let (a, b) = mem::take(self).split_at_mut(amt);
a.copy_from_slice(&buf[..amt]);
Expand Down
4 changes: 2 additions & 2 deletions embedded-io/src/impls/slice_ref.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{BufRead, ErrorType, Read, ReadReady};
use crate::{BufRead, ErrorKind, ErrorType, Read, ReadReady};

impl ErrorType for &[u8] {
type Error = core::convert::Infallible;
type Error = ErrorKind;
}

/// Read is implemented for `&[u8]` by copying from the slice.
Expand Down
4 changes: 2 additions & 2 deletions embedded-io/src/impls/vec.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{ErrorType, Write};
use crate::{ErrorKind, ErrorType, Write};
use alloc::vec::Vec;

#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
impl ErrorType for Vec<u8> {
type Error = core::convert::Infallible;
type Error = ErrorKind;
}

#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
Expand Down
97 changes: 49 additions & 48 deletions embedded-io/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ pub enum ErrorKind {
///
/// [`InvalidInput`]: ErrorKind::InvalidInput
InvalidData,
/// The underlying storage (typically, a filesystem) is full.
///
/// This does not include out of quota errors.
StorageFull,
/// The I/O operation's timeout expired, causing it to be canceled.
TimedOut,
/// This operation was interrupted.
Expand All @@ -109,6 +113,13 @@ pub enum ErrorKind {
///
/// This means that the operation can never succeed.
Unsupported,
/// An error returned when an operation could not be completed because an
/// "end of file" was reached prematurely.
///
/// This typically means that an operation could only succeed if it read a
/// particular number of bytes but only a smaller number of bytes could be
/// read.
UnexpectedEof,
/// An operation could not be completed, because it failed
/// to allocate enough memory.
OutOfMemory,
Expand All @@ -133,9 +144,11 @@ impl From<ErrorKind> for std::io::ErrorKind {
ErrorKind::AlreadyExists => std::io::ErrorKind::AlreadyExists,
ErrorKind::InvalidInput => std::io::ErrorKind::InvalidInput,
ErrorKind::InvalidData => std::io::ErrorKind::InvalidData,
ErrorKind::StorageFull => std::io::ErrorKind::StorageFull,
ErrorKind::TimedOut => std::io::ErrorKind::TimedOut,
ErrorKind::Interrupted => std::io::ErrorKind::Interrupted,
ErrorKind::Unsupported => std::io::ErrorKind::Unsupported,
ErrorKind::UnexpectedEof => std::io::ErrorKind::UnexpectedEof,
ErrorKind::OutOfMemory => std::io::ErrorKind::OutOfMemory,
_ => std::io::ErrorKind::Other,
}
Expand All @@ -159,9 +172,11 @@ impl From<std::io::ErrorKind> for ErrorKind {
std::io::ErrorKind::AlreadyExists => ErrorKind::AlreadyExists,
std::io::ErrorKind::InvalidInput => ErrorKind::InvalidInput,
std::io::ErrorKind::InvalidData => ErrorKind::InvalidData,
std::io::ErrorKind::StorageFull => ErrorKind::StorageFull,
std::io::ErrorKind::TimedOut => ErrorKind::TimedOut,
std::io::ErrorKind::Interrupted => ErrorKind::Interrupted,
std::io::ErrorKind::Unsupported => ErrorKind::Unsupported,
std::io::ErrorKind::UnexpectedEof => ErrorKind::UnexpectedEof,
std::io::ErrorKind::OutOfMemory => ErrorKind::OutOfMemory,
_ => ErrorKind::Other,
}
Expand All @@ -172,17 +187,11 @@ impl From<std::io::ErrorKind> for ErrorKind {
///
/// This trait allows generic code to do limited inspecting of errors,
/// to react differently to different kinds.
pub trait Error: core::error::Error {
pub trait Error: core::error::Error + From<ErrorKind> {
/// Get the kind of this error.
fn kind(&self) -> ErrorKind;
}

impl Error for core::convert::Infallible {
fn kind(&self) -> ErrorKind {
match *self {}
}
}

impl Error for ErrorKind {
fn kind(&self) -> ErrorKind {
*self
Expand All @@ -205,6 +214,14 @@ impl Error for std::io::Error {
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl From<ErrorKind> for std::io::Error {
fn from(value: ErrorKind) -> Self {
std::io::ErrorKind::from(value).into()
}
}

/// Base trait for all IO traits, defining the error type.
///
/// All IO operations of all traits return the error defined in this trait.
Expand All @@ -223,44 +240,6 @@ impl<T: ?Sized + ErrorType> ErrorType for &mut T {
type Error = T::Error;
}

/// Error returned by [`Read::read_exact`]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReadExactError<E> {
/// An EOF error was encountered before reading the exact amount of requested bytes.
UnexpectedEof,
/// Error returned by the inner Read.
Other(E),
}

impl<E> From<E> for ReadExactError<E> {
fn from(err: E) -> Self {
Self::Other(err)
}
}

#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl From<ReadExactError<std::io::Error>> for std::io::Error {
fn from(err: ReadExactError<std::io::Error>) -> Self {
match err {
ReadExactError::UnexpectedEof => std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"UnexpectedEof".to_owned(),
),
ReadExactError::Other(e) => std::io::Error::new(e.kind(), format!("{e:?}")),
}
}
}

impl<E: fmt::Debug> fmt::Display for ReadExactError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self:?}")
}
}

impl<E: fmt::Debug> core::error::Error for ReadExactError<E> {}

/// Errors that could be returned by `Write` on `&mut [u8]`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
Expand Down Expand Up @@ -326,6 +305,15 @@ pub trait Read: ErrorType {
///
/// If `buf.len() == 0`, `read` returns without blocking, with either `Ok(0)` or an error.
/// The `Ok(0)` doesn't indicate EOF, unlike when called with a non-empty buffer.
///
/// # Errors
///
/// If this function encounters any form of I/O or other error, an error
/// variant will be returned. If an error is returned then it must be
/// guaranteed that no bytes were read.
///
/// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
/// operation should be retried if there is nothing else to do.
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;

/// Read the exact number of bytes required to fill `buf`.
Expand All @@ -336,18 +324,31 @@ pub trait Read: ErrorType {
/// If you are using [`ReadReady`] to avoid blocking, you should not use this function.
/// `ReadReady::read_ready()` returning true only guarantees the first call to `read()` will
/// not block, so this function may still block in subsequent calls.
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), ReadExactError<Self::Error>> {
////
/// /// # Errors
///
/// If this function encounters an "end of file" before completely filling
/// the buffer, it returns an error of the kind [`ErrorKind::UnexpectedEof`].
/// The contents of `buf` are unspecified in this case.
///
/// If any other read error is encountered then this function immediately
/// returns. The contents of `buf` are unspecified in this case.
///
/// If this function returns an error, it is unspecified how many bytes it
/// has read, but it will never read more than would be necessary to
/// completely fill the buffer.
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
while !buf.is_empty() {
match self.read(buf) {
Ok(0) => break,
Ok(n) => buf = &mut buf[n..],
Err(e) => return Err(ReadExactError::Other(e)),
Err(e) => return Err(e),
}
}
if buf.is_empty() {
Ok(())
} else {
Err(ReadExactError::UnexpectedEof)
Err(ErrorKind::UnexpectedEof.into())
}
}
}
Expand Down
Loading