Skip to content

Commit

Permalink
Merge #407
Browse files Browse the repository at this point in the history
407: async: switch to async-fn-in-traits, release v0.2.0-alpha.0 r=eldruin a=Dirbaio

Latest Rust nightlies have somewhat usable async-fn-in-trait support already! 🎉 

embassy-nrf updated here embassy-rs/embassy#974

Paprecuts encountered:

- there's this annoying error [playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=f04fca1f2a3d643c323fb49c05bd3ed3), workaround is to use the concrete type instead of `Self::Error`. This is a limitation of all `async fn`s, not just in traits, but it hits especially hard within traits, so I dunno if there's plans to improve it.

> `async fn` return type cannot contain a projection or `Self` that references lifetimes from a parent scope

- The SpiDevice trait ICEs, issue filed rust-lang/rust#102310
- default methods don't work, but there's a PR already rust-lang/rust#102308

Due to the last 2 I've left `SpiDevice` alone for now.

Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
  • Loading branch information
bors[bot] and Dirbaio authored Nov 23, 2022
2 parents aa86d21 + ad0b9f2 commit 812471b
Show file tree
Hide file tree
Showing 9 changed files with 95 additions and 261 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/clippy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
profile: minimal
# embedded-hal-async needs nightly.
# Use a pinned version to avoid spontaneous breakages (new clippy lints are added often)
toolchain: nightly-2022-09-25
toolchain: nightly-2022-11-22
override: true
components: clippy
- run: cargo clippy -- --deny=warnings
6 changes: 5 additions & 1 deletion embedded-hal-async/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [v0.2.0-alpha.0] - 2022-11-23

- Switch all traits to use [`async_fn_in_trait`](https://blog.rust-lang.org/inside-rust/2022/11/17/async-fn-in-trait-nightly.html) (AFIT). Requires `nightly-2022-11-22` or newer.

## [v0.1.0-alpha.3] - 2022-10-26

Expand Down Expand Up @@ -34,7 +37,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
First release to crates.io


[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...HEAD
[Unreleased]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.2.0-alpha.0...HEAD
[v0.2.0-alpha.0]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.3...embedded-hal-async-v0.2.0-alpha.0
[v0.1.0-alpha.3]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.2...embedded-hal-async-v0.1.0-alpha.3
[v0.1.0-alpha.2]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.1...embedded-hal-async-v0.1.0-alpha.2
[v0.1.0-alpha.1]: https://github.com/rust-embedded/embedded-hal/compare/embedded-hal-async-v0.1.0-alpha.0...embedded-hal-async-v0.1.0-alpha.1
Expand Down
2 changes: 1 addition & 1 deletion embedded-hal-async/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0"
name = "embedded-hal-async"
readme = "README.md"
repository = "https://github.com/rust-embedded/embedded-hal"
version = "0.1.0-alpha.3"
version = "0.2.0-alpha.0"

[dependencies]
embedded-hal = { version = "=1.0.0-alpha.9", path = "../embedded-hal" }
28 changes: 6 additions & 22 deletions embedded-hal-async/src/delay.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,17 @@
//! Delays

use core::future::Future;

/// Microsecond delay
pub trait DelayUs {
/// Enumeration of errors
type Error: core::fmt::Debug;

/// The future returned by the `delay_us` function.
type DelayUsFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;

/// Pauses execution for at minimum `us` microseconds. Pause can be longer
/// if the implementation requires it due to precision/timing issues.
fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_>;

/// The future returned by the `delay_ms` function.
type DelayMsFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error>;

/// Pauses execution for at minimum `ms` milliseconds. Pause can be longer
/// if the implementation requires it due to precision/timing issues.
fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_>;
async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error>;
}

impl<T> DelayUs for &mut T
Expand All @@ -32,15 +20,11 @@ where
{
type Error = T::Error;

type DelayUsFuture<'a> = T::DelayUsFuture<'a> where Self: 'a;

fn delay_us(&mut self, us: u32) -> Self::DelayUsFuture<'_> {
T::delay_us(self, us)
async fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
T::delay_us(self, us).await
}

type DelayMsFuture<'a> = T::DelayMsFuture<'a> where Self: 'a;

fn delay_ms(&mut self, ms: u32) -> Self::DelayMsFuture<'_> {
T::delay_ms(self, ms)
async fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> {
T::delay_ms(self, ms).await
}
}
67 changes: 15 additions & 52 deletions embedded-hal-async/src/digital.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,93 +16,56 @@
//! }
//! ```

use core::future::Future;

/// Asynchronously wait for GPIO pin state.
pub trait Wait: embedded_hal::digital::ErrorType {
/// The future returned by the `wait_for_high` function.
type WaitForHighFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;

/// Wait until the pin is high. If it is already high, return immediately.
///
/// # Note for implementers
/// The pin may have switched back to low before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_high(&mut self) -> Self::WaitForHighFuture<'_>;

/// The future returned by `wait_for_low`.
type WaitForLowFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn wait_for_high(&mut self) -> Result<(), Self::Error>;

/// Wait until the pin is low. If it is already low, return immediately.
///
/// # Note for implementers
/// The pin may have switched back to high before the task was run after
/// being woken. The future should still resolve in that case.
fn wait_for_low(&mut self) -> Self::WaitForLowFuture<'_>;

/// The future returned from `wait_for_rising_edge`.
type WaitForRisingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn wait_for_low(&mut self) -> Result<(), Self::Error>;

/// Wait for the pin to undergo a transition from low to high.
///
/// If the pin is already high, this does *not* return immediately, it'll wait for the
/// pin to go low and then high again.
fn wait_for_rising_edge(&mut self) -> Self::WaitForRisingEdgeFuture<'_>;

/// The future returned from `wait_for_falling_edge`.
type WaitForFallingEdgeFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error>;

/// Wait for the pin to undergo a transition from high to low.
///
/// If the pin is already low, this does *not* return immediately, it'll wait for the
/// pin to go high and then low again.
fn wait_for_falling_edge(&mut self) -> Self::WaitForFallingEdgeFuture<'_>;

/// The future returned from `wait_for_any_edge`.
type WaitForAnyEdgeFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error>;

/// Wait for the pin to undergo any transition, i.e low to high OR high to low.
fn wait_for_any_edge(&mut self) -> Self::WaitForAnyEdgeFuture<'_>;
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error>;
}

impl<T: Wait> Wait for &mut T {
type WaitForHighFuture<'a> = T::WaitForHighFuture<'a> where Self: 'a;

fn wait_for_high(&mut self) -> Self::WaitForHighFuture<'_> {
T::wait_for_high(self)
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
T::wait_for_high(self).await
}

type WaitForLowFuture<'a> = T::WaitForLowFuture<'a> where Self: 'a;

fn wait_for_low(&mut self) -> Self::WaitForLowFuture<'_> {
T::wait_for_low(self)
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
T::wait_for_low(self).await
}

type WaitForRisingEdgeFuture<'a> = T::WaitForRisingEdgeFuture<'a> where Self: 'a;

fn wait_for_rising_edge(&mut self) -> Self::WaitForRisingEdgeFuture<'_> {
T::wait_for_rising_edge(self)
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
T::wait_for_rising_edge(self).await
}

type WaitForFallingEdgeFuture<'a> = T::WaitForFallingEdgeFuture<'a> where Self: 'a;

fn wait_for_falling_edge(&mut self) -> Self::WaitForFallingEdgeFuture<'_> {
T::wait_for_falling_edge(self)
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
T::wait_for_falling_edge(self).await
}

type WaitForAnyEdgeFuture<'a> = T::WaitForAnyEdgeFuture<'a> where Self: 'a;

fn wait_for_any_edge(&mut self) -> Self::WaitForAnyEdgeFuture<'_> {
T::wait_for_any_edge(self)
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
T::wait_for_any_edge(self).await
}
}
62 changes: 16 additions & 46 deletions embedded-hal-async/src/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,13 @@
//! Since 7-bit addressing is the mode of the majority of I2C devices,
//! `SevenBitAddress` has been set as default mode and thus can be omitted if desired.

use core::future::Future;
pub use embedded_hal::i2c::Operation;
pub use embedded_hal::i2c::{
AddressMode, Error, ErrorKind, ErrorType, NoAcknowledgeSource, SevenBitAddress, TenBitAddress,
};

/// Async i2c
pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// Future returned by the `read` method.
type ReadFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;

/// Reads enough bytes from slave with `address` to fill `buffer`
///
/// # I2C Events (contract)
Expand All @@ -47,12 +41,7 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// - `MAK` = master acknowledge
/// - `NMAK` = master no acknowledge
/// - `SP` = stop condition
fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a>;

/// Future returned by the `write` method.
type WriteFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Result<(), Self::Error>;

/// Writes bytes to slave with address `address`
///
Expand All @@ -70,12 +59,7 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// - `SAK` = slave acknowledge
/// - `Bi` = ith byte of data
/// - `SP` = stop condition
fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a>;

/// Future returned by the `write_read` method.
type WriteReadFuture<'a>: Future<Output = Result<(), Self::Error>>
where
Self: 'a;
async fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Result<(), Self::Error>;

/// Writes bytes to slave with address `address` and then reads enough bytes to fill `read` *in a
/// single transaction*.
Expand All @@ -99,18 +83,12 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// - `MAK` = master acknowledge
/// - `NMAK` = master no acknowledge
/// - `SP` = stop condition
fn write_read<'a>(
async fn write_read<'a>(
&'a mut self,
address: A,
write: &'a [u8],
read: &'a mut [u8],
) -> Self::WriteReadFuture<'a>;

/// Future returned by the `transaction` method.
type TransactionFuture<'a, 'b>: Future<Output = Result<(), Self::Error>>
where
Self: 'a,
'b: 'a;
) -> Result<(), Self::Error>;

/// Execute the provided operations on the I2C bus as a single transaction.
///
Expand All @@ -125,44 +103,36 @@ pub trait I2c<A: AddressMode = SevenBitAddress>: ErrorType {
/// - `SAD+R/W` = slave address followed by bit 1 to indicate reading or 0 to indicate writing
/// - `SR` = repeated start condition
/// - `SP` = stop condition
fn transaction<'a, 'b>(
async fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b>;
) -> Result<(), Self::Error>;
}

impl<A: AddressMode, T: I2c<A>> I2c<A> for &mut T {
type ReadFuture<'a> = T::ReadFuture<'a> where Self: 'a;

fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Self::ReadFuture<'a> {
T::read(self, address, buffer)
async fn read<'a>(&'a mut self, address: A, buffer: &'a mut [u8]) -> Result<(), Self::Error> {
T::read(self, address, buffer).await
}

type WriteFuture<'a> = T::WriteFuture<'a> where Self: 'a;

fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Self::WriteFuture<'a> {
T::write(self, address, bytes)
async fn write<'a>(&'a mut self, address: A, bytes: &'a [u8]) -> Result<(), Self::Error> {
T::write(self, address, bytes).await
}

type WriteReadFuture<'a> = T::WriteReadFuture<'a> where Self: 'a;

fn write_read<'a>(
async fn write_read<'a>(
&'a mut self,
address: A,
bytes: &'a [u8],
buffer: &'a mut [u8],
) -> Self::WriteReadFuture<'a> {
T::write_read(self, address, bytes, buffer)
) -> Result<(), Self::Error> {
T::write_read(self, address, bytes, buffer).await
}

type TransactionFuture<'a, 'b> = T::TransactionFuture<'a, 'b> where Self: 'a, 'b: 'a;

fn transaction<'a, 'b>(
async fn transaction<'a, 'b>(
&'a mut self,
address: A,
operations: &'a mut [Operation<'b>],
) -> Self::TransactionFuture<'a, 'b> {
T::transaction(self, address, operations)
) -> Result<(), Self::Error> {
T::transaction(self, address, operations).await
}
}
3 changes: 2 additions & 1 deletion embedded-hal-async/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@

#![warn(missing_docs)]
#![no_std]
#![feature(type_alias_impl_trait)]
#![allow(incomplete_features)]
#![feature(async_fn_in_trait, impl_trait_projections)]

pub mod delay;
pub mod digital;
Expand Down
Loading

0 comments on commit 812471b

Please sign in to comment.