Skip to content

Commit

Permalink
embedded-io-adapters: Add adapters for embedded-storage and embedded-…
Browse files Browse the repository at this point in the history
…storage-async
  • Loading branch information
Aurélien Jacobs committed Jun 14, 2024
1 parent 1c585d4 commit 2d0f56d
Show file tree
Hide file tree
Showing 6 changed files with 332 additions and 2 deletions.
2 changes: 1 addition & 1 deletion embedded-io-adapters/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

Add unreleased changes here
- Add adapters for the `embedded-storage` and `embedded-storage-async` traits.

## 0.6.1 - 2023-11-28

Expand Down
6 changes: 6 additions & 0 deletions embedded-io-adapters/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ categories = [
std = ["embedded-io/std"]
tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std"]
futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"]
embedded-storage = ["dep:embedded-storage"]
embedded-storage-async = ["embedded-storage", "dep:embedded-storage-async", "dep:embedded-io-async"]
defmt-03 = ["dep:defmt-03"]

[dependencies]
embedded-io = { version = "0.6", path = "../embedded-io" }
embedded-io-async = { version = "0.6.1", path = "../embedded-io-async", optional = true }

futures = { version = "0.3.21", features = ["std"], default-features = false, optional = true }
tokio = { version = "1", features = ["io-util"], default-features = false, optional = true }
embedded-storage = { version = "0.3.1", optional = true }
embedded-storage-async = { version = "0.4.1", optional = true }
defmt-03 = { package = "defmt", version = "0.3", optional = true }

[package.metadata.docs.rs]
features = ["std", "tokio-1", "futures-03"]
Expand Down
5 changes: 4 additions & 1 deletion embedded-io-adapters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ This allows using these adapters when using combinations of traits, like `Read+W
For `embedded-io`:

- [`std::io`](https://doc.rust-lang.org/stable/std/io/index.html) traits. Needs the `std` feature.
- [`embedded-storage`](https://crates.io/crates/embedded-storage) traits. Needs the `embedded-storage` feature.

For `embedded-io-async`:

- [`futures` 0.3](https://crates.io/crates/futures) traits. Needs the `futures-03` feature.
- [`tokio` 1.x](https://crates.io/crates/tokio) traits. Needs the `tokio-1` feature.
- [`embedded-storage-async`](https://crates.io/crates/embedded-storage-async) traits. Needs the `embedded-storage-async` feature.

## Minimum Supported Rust Version (MSRV)

Expand All @@ -34,7 +36,8 @@ compile with older versions but that may change in any new patch release.

See [here](../docs/msrv.md) for details on how the MSRV may be upgraded.

Enabling any of the `tokio-*` or `futures-*` Cargo features requires Rust 1.75 or higher.
Enabling any of the `tokio-*`, `futures-*` or `embedded-storage-async` Cargo features
requires Rust 1.75 or higher.

## License

Expand Down
174 changes: 174 additions & 0 deletions embedded-io-adapters/src/embedded_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
//! Adapters to/from `embedded_storage` traits.
// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`.
#[cfg(feature = "defmt-03")]
use defmt_03 as defmt;

use embedded_io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
use embedded_storage::{ReadStorage, Storage};

/// Adapter from `embedded_storage` traits.
#[derive(Clone)]
pub struct FromEmbeddedStorage<T: ?Sized> {
position: u32,
inner: T,
}

impl<T> FromEmbeddedStorage<T> {
/// Create a new adapter.
pub fn new(inner: T) -> Self {
Self { position: 0, inner }
}

/// Consume the adapter, returning the inner object.
pub fn into_inner(self) -> T {
self.inner
}
}

impl<T: ?Sized> FromEmbeddedStorage<T> {
/// Borrow the inner object.
pub fn inner(&self) -> &T {
&self.inner
}

/// Mutably borrow the inner object.
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T: ?Sized> embedded_io::ErrorType for FromEmbeddedStorage<T> {
type Error = StorageIOError;
}

impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Read
for FromEmbeddedStorage<T>
{
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.inner.read(self.position, buf).map_err(|e| e.into())?;
self.position += buf.len() as u32;
Ok(buf.len())
}
}

impl<T: Storage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Write
for FromEmbeddedStorage<T>
{
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.inner.write(self.position, buf).map_err(|e| e.into())?;
self.position += buf.len() as u32;
Ok(buf.len())
}

fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}

impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Seek
for FromEmbeddedStorage<T>
{
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
let new_position = match pos {
SeekFrom::Start(pos) => pos as i64,
SeekFrom::End(offset) => self.inner.capacity() as i64 + offset,
SeekFrom::Current(offset) => self.position as i64 + offset,
};
self.position = new_position as u32;
Ok(self.position as u64)
}
}

/// Adapter to `embedded_storage` traits.
#[derive(Clone)]
pub struct ToEmbeddedStorage<T: ?Sized> {
capacity: usize,
inner: T,
}

impl<T: Seek> ToEmbeddedStorage<T> {
/// Create a new adapter.
pub fn new(mut inner: T) -> Self {
let capacity = inner.seek(SeekFrom::End(0)).unwrap() as usize;
Self { inner, capacity }
}

/// Consume the adapter, returning the inner object.
pub fn into_inner(self) -> T {
self.inner
}
}

impl<T: ?Sized> ToEmbeddedStorage<T> {
/// Borrow the inner object.
pub fn inner(&self) -> &T {
&self.inner
}

/// Mutably borrow the inner object.
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T: Read + Seek + ?Sized> ReadStorage for ToEmbeddedStorage<T> {
type Error = T::Error;

fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.inner.seek(SeekFrom::Start(offset as u64))?;
let mut read = 0;
while read < bytes.len() {
read += self.inner.read(&mut bytes[read..])?;
}
Ok(())
}

fn capacity(&self) -> usize {
self.capacity
}
}

impl<T: Read + Write + Seek + ?Sized> Storage for ToEmbeddedStorage<T> {
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.inner.seek(SeekFrom::Start(offset as u64))?;
let mut written = 0;
while written < bytes.len() {
written += self.inner.write(&bytes[written..])?;
}
Ok(())
}
}

/// An error type that is implementing embedded_io::Error and that the concret
/// Storage::Error type should be able to convert to, to allow error compatibility
/// with the embedded_io layer.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
pub struct StorageIOError {
kind: ErrorKind,
}

impl StorageIOError {
/// Create a new StorageIOError.
pub fn new(kind: ErrorKind) -> Self {
Self { kind }
}
}

impl Error for StorageIOError {
fn kind(&self) -> ErrorKind {
self.kind
}
}

impl<E: NorFlashError> From<E> for StorageIOError {
fn from(value: E) -> Self {
match value.kind() {
NorFlashErrorKind::NotAligned => Self::new(ErrorKind::InvalidInput),
NorFlashErrorKind::OutOfBounds => Self::new(ErrorKind::InvalidInput),
_ => Self::new(ErrorKind::Other),
}
}
}
139 changes: 139 additions & 0 deletions embedded-io-adapters/src/embedded_storage_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! Adapters to/from `embedded_storage_async` traits.
use embedded_io_async::{Read, Seek, SeekFrom, Write};
use embedded_storage_async::{ReadStorage, Storage};

pub use crate::embedded_storage::StorageIOError;

/// Adapter from `embedded_storage_async` traits.
#[derive(Clone)]
pub struct FromEmbeddedStorage<T: ?Sized> {
position: u32,
inner: T,
}

impl<T> FromEmbeddedStorage<T> {
/// Create a new adapter.
pub fn new(inner: T) -> Self {
Self { position: 0, inner }
}

/// Consume the adapter, returning the inner object.
pub fn into_inner(self) -> T {
self.inner
}
}

impl<T: ?Sized> FromEmbeddedStorage<T> {
/// Borrow the inner object.
pub fn inner(&self) -> &T {
&self.inner
}

/// Mutably borrow the inner object.
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T: ?Sized> embedded_io_async::ErrorType for FromEmbeddedStorage<T> {
type Error = StorageIOError;
}

impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> Read for FromEmbeddedStorage<T> {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.inner
.read(self.position, buf)
.await
.map_err(|e| e.into())?;
self.position += buf.len() as u32;
Ok(buf.len())
}
}

impl<T: Storage<Error = E> + ?Sized, E: Into<StorageIOError>> Write for FromEmbeddedStorage<T> {
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.inner
.write(self.position, buf)
.await
.map_err(|e| e.into())?;
self.position += buf.len() as u32;
Ok(buf.len())
}

async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}

impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> Seek for FromEmbeddedStorage<T> {
async fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
let new_position = match pos {
SeekFrom::Start(pos) => pos as i64,
SeekFrom::End(offset) => self.inner.capacity() as i64 + offset,
SeekFrom::Current(offset) => self.position as i64 + offset,
};
self.position = new_position as u32;
Ok(self.position as u64)
}
}

/// Adapter to `embedded_storage_async` traits.
#[derive(Clone)]
pub struct ToEmbeddedStorage<T: ?Sized> {
capacity: usize,
inner: T,
}

impl<T: Seek> ToEmbeddedStorage<T> {
/// Create a new adapter.
pub async fn new(mut inner: T) -> Self {
let capacity = inner.seek(SeekFrom::End(0)).await.unwrap() as usize;
Self { inner, capacity }
}

/// Consume the adapter, returning the inner object.
pub fn into_inner(self) -> T {
self.inner
}
}

impl<T: ?Sized> ToEmbeddedStorage<T> {
/// Borrow the inner object.
pub fn inner(&self) -> &T {
&self.inner
}

/// Mutably borrow the inner object.
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T: Read + Seek + ?Sized> ReadStorage for ToEmbeddedStorage<T> {
type Error = T::Error;

async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.inner.seek(SeekFrom::Start(offset as u64)).await?;
let mut read = 0;
while read < bytes.len() {
read += self.inner.read(&mut bytes[read..]).await?;
}
Ok(())
}

fn capacity(&self) -> usize {
self.capacity
}
}

impl<T: Read + Write + Seek + ?Sized> Storage for ToEmbeddedStorage<T> {
async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.inner.seek(SeekFrom::Start(offset as u64)).await?;
let mut written = 0;
while written < bytes.len() {
written += self.inner.write(&bytes[written..]).await?;
}
Ok(())
}
}
8 changes: 8 additions & 0 deletions embedded-io-adapters/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@ pub mod futures_03;
#[cfg(feature = "tokio-1")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-1")))]
pub mod tokio_1;

#[cfg(feature = "embedded-storage")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-storage")))]
pub mod embedded_storage;

#[cfg(feature = "embedded-storage-async")]
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-storage-async")))]
pub mod embedded_storage_async;

0 comments on commit 2d0f56d

Please sign in to comment.