Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

embedded-io-adapters: Add adapters for embedded-storage and embedded-storage-async #609

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
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 = { git = "https://github.com/rust-embedded-community/embedded-storage.git", optional = true }
embedded-storage-async = { git = "https://github.com/rust-embedded-community/embedded-storage.git", 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(())
}
}
Loading
Loading