nb/lib.rs
1//! Minimal and reusable non-blocking I/O layer
2//!
3//! The ultimate goal of this crate is *code reuse*. With this crate you can
4//! write *core* I/O APIs that can then be adapted to operate in either blocking
5//! or non-blocking manner. Furthermore those APIs are not tied to a particular
6//! asynchronous model and can be adapted to work with the `futures` model or
7//! with the `async` / `await` model.
8//!
9//! # Core idea
10//!
11//! The [`WouldBlock`](enum.Error.html) error variant signals that the operation
12//! can't be completed *right now* and would need to block to complete.
13//! [`WouldBlock`](enum.Error.html) is a special error in the sense that's not
14//! *fatal*; the operation can still be completed by retrying again later.
15//!
16//! [`nb::Result`](type.Result.html) is based on the API of
17//! [`std::io::Result`](https://doc.rust-lang.org/std/io/type.Result.html),
18//! which has a `WouldBlock` variant in its
19//! [`ErrorKind`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html).
20//!
21//! We can map [`WouldBlock`](enum.Error.html) to different blocking and
22//! non-blocking models:
23//!
24//! - In blocking mode: [`WouldBlock`](enum.Error.html) means try again right
25//! now (i.e. busy wait)
26//! - In `futures` mode: [`WouldBlock`](enum.Error.html) means
27//! [`Async::NotReady`](https://docs.rs/futures)
28//! - In `await` mode: [`WouldBlock`](enum.Error.html) means `yield`
29//! (suspend the generator)
30//!
31//! # How to use this crate
32//!
33//! Application specific errors can be put inside the `Other` variant in the
34//! [`nb::Error`](enum.Error.html) enum.
35//!
36//! So in your API instead of returning `Result<T, MyError>` return
37//! `nb::Result<T, MyError>`
38//!
39//! ```
40//! enum MyError {
41//! ThisError,
42//! ThatError,
43//! // ..
44//! }
45//!
46//! // This is a blocking function, so it returns a normal `Result`
47//! fn before() -> Result<(), MyError> {
48//! // ..
49//! # Ok(())
50//! }
51//!
52//! // This is now a potentially (read: *non*) blocking function so it returns `nb::Result`
53//! // instead of blocking
54//! fn after() -> nb::Result<(), MyError> {
55//! // ..
56//! # Ok(())
57//! }
58//! ```
59//!
60//! You can use `Infallible` to signal that some API has no fatal
61//! errors but may block:
62//!
63//! ```
64//! use core::convert::Infallible;
65//!
66//! // This returns `Ok(())` or `Err(nb::Error::WouldBlock)`
67//! fn maybe_blocking_api() -> nb::Result<(), Infallible> {
68//! // ..
69//! # Ok(())
70//! }
71//! ```
72//!
73//! Once your API uses [`nb::Result`] you can leverage the [`block!`], macro
74//! to adapt it for blocking operation, or handle scheduling yourself.
75//!
76//! [`block!`]: macro.block.html
77//! [`nb::Result`]: type.Result.html
78//!
79//! # Examples
80//!
81//! ## A Core I/O API
82//!
83//! Imagine the code (crate) below represents a Hardware Abstraction Layer for some microcontroller
84//! (or microcontroller family).
85//!
86//! *In this and the following examples let's assume for simplicity that peripherals are treated
87//! as global singletons and that no preemption is possible (i.e. interrupts are disabled).*
88//!
89//! ```
90//! # use core::convert::Infallible;
91//! // This is the `hal` crate
92//! use nb;
93//!
94//! /// An LED
95//! pub struct Led;
96//!
97//! impl Led {
98//! pub fn off(&self) {
99//! // ..
100//! }
101//! pub fn on(&self) {
102//! // ..
103//! }
104//! }
105//!
106//! /// Serial interface
107//! pub struct Serial;
108//! pub enum Error {
109//! Overrun,
110//! // ..
111//! }
112//!
113//! impl Serial {
114//! /// Reads a single byte from the serial interface
115//! pub fn read(&self) -> nb::Result<u8, Error> {
116//! // ..
117//! # Ok(0)
118//! }
119//!
120//! /// Writes a single byte to the serial interface
121//! pub fn write(&self, byte: u8) -> nb::Result<(), Error> {
122//! // ..
123//! # Ok(())
124//! }
125//! }
126//!
127//! /// A timer used for timeouts
128//! pub struct Timer;
129//!
130//! impl Timer {
131//! /// Waits until the timer times out
132//! pub fn wait(&self) -> nb::Result<(), Infallible> {
133//! //^ NOTE the `Infallible` indicates that this operation can block but has no
134//! // other form of error
135//!
136//! // ..
137//! # Ok(())
138//! }
139//! }
140//! ```
141//!
142//! ## Blocking mode
143//!
144//! Turn on an LED for one second and *then* loops back serial data.
145//!
146//! ```
147//! use core::convert::Infallible;
148//! use nb::block;
149//!
150//! use hal::{Led, Serial, Timer};
151//!
152//! # fn main() -> Result<(), Infallible> {
153//! // Turn the LED on for one second
154//! Led.on();
155//! block!(Timer.wait())?;
156//! Led.off();
157//!
158//! // Serial interface loopback
159//! # return Ok(());
160//! loop {
161//! let byte = block!(Serial.read())?;
162//! block!(Serial.write(byte))?;
163//! }
164//! # }
165//!
166//! # mod hal {
167//! # use nb;
168//! # use core::convert::Infallible;
169//! # pub struct Led;
170//! # impl Led {
171//! # pub fn off(&self) {}
172//! # pub fn on(&self) {}
173//! # }
174//! # pub struct Serial;
175//! # impl Serial {
176//! # pub fn read(&self) -> nb::Result<u8, Infallible> { Ok(0) }
177//! # pub fn write(&self, _: u8) -> nb::Result<(), Infallible> { Ok(()) }
178//! # }
179//! # pub struct Timer;
180//! # impl Timer {
181//! # pub fn wait(&self) -> nb::Result<(), Infallible> { Ok(()) }
182//! # }
183//! # }
184//! ```
185//!
186//! # Features
187//!
188//! - `defmt-0-3` - unstable feature which adds [`defmt::Format`] impl for [`Error`].
189
190#![no_std]
191
192use core::fmt;
193
194/// A non-blocking result
195pub type Result<T, E> = ::core::result::Result<T, Error<E>>;
196
197/// A non-blocking error
198///
199/// The main use of this enum is to add a `WouldBlock` variant to an existing
200/// error enum.
201#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
202pub enum Error<E> {
203 /// A different kind of error
204 Other(E),
205 /// This operation requires blocking behavior to complete
206 WouldBlock,
207}
208
209#[cfg(feature = "defmt-0-3")]
210impl<E> defmt::Format for Error<E>
211where
212 E: defmt::Format,
213{
214 fn format(&self, f: defmt::Formatter) {
215 match *self {
216 Error::Other(ref e) => defmt::Format::format(e, f),
217 Error::WouldBlock => defmt::write!(f, "WouldBlock",),
218 }
219 }
220}
221
222impl<E> fmt::Debug for Error<E>
223where
224 E: fmt::Debug,
225{
226 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227 match *self {
228 Error::Other(ref e) => fmt::Debug::fmt(e, f),
229 Error::WouldBlock => f.write_str("WouldBlock"),
230 }
231 }
232}
233
234impl<E> Error<E> {
235 /// Maps an `Error<E>` to `Error<T>` by applying a function to a contained
236 /// `Error::Other` value, leaving an `Error::WouldBlock` value untouched.
237 pub fn map<T, F>(self, op: F) -> Error<T>
238 where
239 F: FnOnce(E) -> T,
240 {
241 match self {
242 Error::Other(e) => Error::Other(op(e)),
243 Error::WouldBlock => Error::WouldBlock,
244 }
245 }
246}
247
248impl<E> From<E> for Error<E> {
249 fn from(error: E) -> Error<E> {
250 Error::Other(error)
251 }
252}
253
254/// Turns the non-blocking expression `$e` into a blocking operation.
255///
256/// This is accomplished by continuously calling the expression `$e` until it no
257/// longer returns `Error::WouldBlock`
258///
259/// # Input
260///
261/// An expression `$e` that evaluates to `nb::Result<T, E>`
262///
263/// # Output
264///
265/// - `Ok(t)` if `$e` evaluates to `Ok(t)`
266/// - `Err(e)` if `$e` evaluates to `Err(nb::Error::Other(e))`
267#[macro_export]
268macro_rules! block {
269 ($e:expr) => {
270 loop {
271 #[allow(unreachable_patterns)]
272 match $e {
273 Err($crate::Error::Other(e)) =>
274 {
275 #[allow(unreachable_code)]
276 break Err(e)
277 }
278 Err($crate::Error::WouldBlock) => {}
279 Ok(x) => break Ok(x),
280 }
281 }
282 };
283}