Skip to content

Commit 2d0f56d

Browse files
author
Aurélien Jacobs
committed
embedded-io-adapters: Add adapters for embedded-storage and embedded-storage-async
1 parent 1c585d4 commit 2d0f56d

File tree

6 files changed

+332
-2
lines changed

6 files changed

+332
-2
lines changed

embedded-io-adapters/CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

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

1212
## 0.6.1 - 2023-11-28
1313

embedded-io-adapters/Cargo.toml

+6
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ categories = [
1616
std = ["embedded-io/std"]
1717
tokio-1 = ["std", "dep:tokio", "dep:embedded-io-async", "embedded-io-async?/std"]
1818
futures-03 = ["std", "dep:futures", "dep:embedded-io-async", "embedded-io-async?/std"]
19+
embedded-storage = ["dep:embedded-storage"]
20+
embedded-storage-async = ["embedded-storage", "dep:embedded-storage-async", "dep:embedded-io-async"]
21+
defmt-03 = ["dep:defmt-03"]
1922

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

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

2733
[package.metadata.docs.rs]
2834
features = ["std", "tokio-1", "futures-03"]

embedded-io-adapters/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ This allows using these adapters when using combinations of traits, like `Read+W
2121
For `embedded-io`:
2222

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

2526
For `embedded-io-async`:
2627

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

3032
## Minimum Supported Rust Version (MSRV)
3133

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

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

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

3942
## License
4043

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//! Adapters to/from `embedded_storage` traits.
2+
3+
// needed to prevent defmt macros from breaking, since they emit code that does `defmt::blahblah`.
4+
#[cfg(feature = "defmt-03")]
5+
use defmt_03 as defmt;
6+
7+
use embedded_io::{Error, ErrorKind, Read, Seek, SeekFrom, Write};
8+
use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind};
9+
use embedded_storage::{ReadStorage, Storage};
10+
11+
/// Adapter from `embedded_storage` traits.
12+
#[derive(Clone)]
13+
pub struct FromEmbeddedStorage<T: ?Sized> {
14+
position: u32,
15+
inner: T,
16+
}
17+
18+
impl<T> FromEmbeddedStorage<T> {
19+
/// Create a new adapter.
20+
pub fn new(inner: T) -> Self {
21+
Self { position: 0, inner }
22+
}
23+
24+
/// Consume the adapter, returning the inner object.
25+
pub fn into_inner(self) -> T {
26+
self.inner
27+
}
28+
}
29+
30+
impl<T: ?Sized> FromEmbeddedStorage<T> {
31+
/// Borrow the inner object.
32+
pub fn inner(&self) -> &T {
33+
&self.inner
34+
}
35+
36+
/// Mutably borrow the inner object.
37+
pub fn inner_mut(&mut self) -> &mut T {
38+
&mut self.inner
39+
}
40+
}
41+
42+
impl<T: ?Sized> embedded_io::ErrorType for FromEmbeddedStorage<T> {
43+
type Error = StorageIOError;
44+
}
45+
46+
impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Read
47+
for FromEmbeddedStorage<T>
48+
{
49+
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
50+
self.inner.read(self.position, buf).map_err(|e| e.into())?;
51+
self.position += buf.len() as u32;
52+
Ok(buf.len())
53+
}
54+
}
55+
56+
impl<T: Storage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Write
57+
for FromEmbeddedStorage<T>
58+
{
59+
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
60+
self.inner.write(self.position, buf).map_err(|e| e.into())?;
61+
self.position += buf.len() as u32;
62+
Ok(buf.len())
63+
}
64+
65+
fn flush(&mut self) -> Result<(), Self::Error> {
66+
Ok(())
67+
}
68+
}
69+
70+
impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> embedded_io::Seek
71+
for FromEmbeddedStorage<T>
72+
{
73+
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
74+
let new_position = match pos {
75+
SeekFrom::Start(pos) => pos as i64,
76+
SeekFrom::End(offset) => self.inner.capacity() as i64 + offset,
77+
SeekFrom::Current(offset) => self.position as i64 + offset,
78+
};
79+
self.position = new_position as u32;
80+
Ok(self.position as u64)
81+
}
82+
}
83+
84+
/// Adapter to `embedded_storage` traits.
85+
#[derive(Clone)]
86+
pub struct ToEmbeddedStorage<T: ?Sized> {
87+
capacity: usize,
88+
inner: T,
89+
}
90+
91+
impl<T: Seek> ToEmbeddedStorage<T> {
92+
/// Create a new adapter.
93+
pub fn new(mut inner: T) -> Self {
94+
let capacity = inner.seek(SeekFrom::End(0)).unwrap() as usize;
95+
Self { inner, capacity }
96+
}
97+
98+
/// Consume the adapter, returning the inner object.
99+
pub fn into_inner(self) -> T {
100+
self.inner
101+
}
102+
}
103+
104+
impl<T: ?Sized> ToEmbeddedStorage<T> {
105+
/// Borrow the inner object.
106+
pub fn inner(&self) -> &T {
107+
&self.inner
108+
}
109+
110+
/// Mutably borrow the inner object.
111+
pub fn inner_mut(&mut self) -> &mut T {
112+
&mut self.inner
113+
}
114+
}
115+
116+
impl<T: Read + Seek + ?Sized> ReadStorage for ToEmbeddedStorage<T> {
117+
type Error = T::Error;
118+
119+
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
120+
self.inner.seek(SeekFrom::Start(offset as u64))?;
121+
let mut read = 0;
122+
while read < bytes.len() {
123+
read += self.inner.read(&mut bytes[read..])?;
124+
}
125+
Ok(())
126+
}
127+
128+
fn capacity(&self) -> usize {
129+
self.capacity
130+
}
131+
}
132+
133+
impl<T: Read + Write + Seek + ?Sized> Storage for ToEmbeddedStorage<T> {
134+
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
135+
self.inner.seek(SeekFrom::Start(offset as u64))?;
136+
let mut written = 0;
137+
while written < bytes.len() {
138+
written += self.inner.write(&bytes[written..])?;
139+
}
140+
Ok(())
141+
}
142+
}
143+
144+
/// An error type that is implementing embedded_io::Error and that the concret
145+
/// Storage::Error type should be able to convert to, to allow error compatibility
146+
/// with the embedded_io layer.
147+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
148+
#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
149+
pub struct StorageIOError {
150+
kind: ErrorKind,
151+
}
152+
153+
impl StorageIOError {
154+
/// Create a new StorageIOError.
155+
pub fn new(kind: ErrorKind) -> Self {
156+
Self { kind }
157+
}
158+
}
159+
160+
impl Error for StorageIOError {
161+
fn kind(&self) -> ErrorKind {
162+
self.kind
163+
}
164+
}
165+
166+
impl<E: NorFlashError> From<E> for StorageIOError {
167+
fn from(value: E) -> Self {
168+
match value.kind() {
169+
NorFlashErrorKind::NotAligned => Self::new(ErrorKind::InvalidInput),
170+
NorFlashErrorKind::OutOfBounds => Self::new(ErrorKind::InvalidInput),
171+
_ => Self::new(ErrorKind::Other),
172+
}
173+
}
174+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//! Adapters to/from `embedded_storage_async` traits.
2+
3+
use embedded_io_async::{Read, Seek, SeekFrom, Write};
4+
use embedded_storage_async::{ReadStorage, Storage};
5+
6+
pub use crate::embedded_storage::StorageIOError;
7+
8+
/// Adapter from `embedded_storage_async` traits.
9+
#[derive(Clone)]
10+
pub struct FromEmbeddedStorage<T: ?Sized> {
11+
position: u32,
12+
inner: T,
13+
}
14+
15+
impl<T> FromEmbeddedStorage<T> {
16+
/// Create a new adapter.
17+
pub fn new(inner: T) -> Self {
18+
Self { position: 0, inner }
19+
}
20+
21+
/// Consume the adapter, returning the inner object.
22+
pub fn into_inner(self) -> T {
23+
self.inner
24+
}
25+
}
26+
27+
impl<T: ?Sized> FromEmbeddedStorage<T> {
28+
/// Borrow the inner object.
29+
pub fn inner(&self) -> &T {
30+
&self.inner
31+
}
32+
33+
/// Mutably borrow the inner object.
34+
pub fn inner_mut(&mut self) -> &mut T {
35+
&mut self.inner
36+
}
37+
}
38+
39+
impl<T: ?Sized> embedded_io_async::ErrorType for FromEmbeddedStorage<T> {
40+
type Error = StorageIOError;
41+
}
42+
43+
impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> Read for FromEmbeddedStorage<T> {
44+
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
45+
self.inner
46+
.read(self.position, buf)
47+
.await
48+
.map_err(|e| e.into())?;
49+
self.position += buf.len() as u32;
50+
Ok(buf.len())
51+
}
52+
}
53+
54+
impl<T: Storage<Error = E> + ?Sized, E: Into<StorageIOError>> Write for FromEmbeddedStorage<T> {
55+
async fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
56+
self.inner
57+
.write(self.position, buf)
58+
.await
59+
.map_err(|e| e.into())?;
60+
self.position += buf.len() as u32;
61+
Ok(buf.len())
62+
}
63+
64+
async fn flush(&mut self) -> Result<(), Self::Error> {
65+
Ok(())
66+
}
67+
}
68+
69+
impl<T: ReadStorage<Error = E> + ?Sized, E: Into<StorageIOError>> Seek for FromEmbeddedStorage<T> {
70+
async fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
71+
let new_position = match pos {
72+
SeekFrom::Start(pos) => pos as i64,
73+
SeekFrom::End(offset) => self.inner.capacity() as i64 + offset,
74+
SeekFrom::Current(offset) => self.position as i64 + offset,
75+
};
76+
self.position = new_position as u32;
77+
Ok(self.position as u64)
78+
}
79+
}
80+
81+
/// Adapter to `embedded_storage_async` traits.
82+
#[derive(Clone)]
83+
pub struct ToEmbeddedStorage<T: ?Sized> {
84+
capacity: usize,
85+
inner: T,
86+
}
87+
88+
impl<T: Seek> ToEmbeddedStorage<T> {
89+
/// Create a new adapter.
90+
pub async fn new(mut inner: T) -> Self {
91+
let capacity = inner.seek(SeekFrom::End(0)).await.unwrap() as usize;
92+
Self { inner, capacity }
93+
}
94+
95+
/// Consume the adapter, returning the inner object.
96+
pub fn into_inner(self) -> T {
97+
self.inner
98+
}
99+
}
100+
101+
impl<T: ?Sized> ToEmbeddedStorage<T> {
102+
/// Borrow the inner object.
103+
pub fn inner(&self) -> &T {
104+
&self.inner
105+
}
106+
107+
/// Mutably borrow the inner object.
108+
pub fn inner_mut(&mut self) -> &mut T {
109+
&mut self.inner
110+
}
111+
}
112+
113+
impl<T: Read + Seek + ?Sized> ReadStorage for ToEmbeddedStorage<T> {
114+
type Error = T::Error;
115+
116+
async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
117+
self.inner.seek(SeekFrom::Start(offset as u64)).await?;
118+
let mut read = 0;
119+
while read < bytes.len() {
120+
read += self.inner.read(&mut bytes[read..]).await?;
121+
}
122+
Ok(())
123+
}
124+
125+
fn capacity(&self) -> usize {
126+
self.capacity
127+
}
128+
}
129+
130+
impl<T: Read + Write + Seek + ?Sized> Storage for ToEmbeddedStorage<T> {
131+
async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
132+
self.inner.seek(SeekFrom::Start(offset as u64)).await?;
133+
let mut written = 0;
134+
while written < bytes.len() {
135+
written += self.inner.write(&bytes[written..]).await?;
136+
}
137+
Ok(())
138+
}
139+
}

embedded-io-adapters/src/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,11 @@ pub mod futures_03;
2626
#[cfg(feature = "tokio-1")]
2727
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-1")))]
2828
pub mod tokio_1;
29+
30+
#[cfg(feature = "embedded-storage")]
31+
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-storage")))]
32+
pub mod embedded_storage;
33+
34+
#[cfg(feature = "embedded-storage-async")]
35+
#[cfg_attr(docsrs, doc(cfg(feature = "embedded-storage-async")))]
36+
pub mod embedded_storage_async;

0 commit comments

Comments
 (0)