| 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 | |
| 192 | use core::fmt; |
| 193 | |
| 194 | /// A non-blocking result |
| 195 | pub 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)] |
| 202 | pub 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" )] |
| 210 | impl<E> defmt::Format for Error<E> |
| 211 | where |
| 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 | |
| 222 | impl<E> fmt::Debug for Error<E> |
| 223 | where |
| 224 | E: fmt::Debug, |
| 225 | { |
| 226 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 227 | match *self { |
| 228 | Error::Other(ref e: &E) => fmt::Debug::fmt(self:e, f), |
| 229 | Error::WouldBlock => f.write_str(data:"WouldBlock" ), |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | impl<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: E) => Error::Other(op(e)), |
| 243 | Error::WouldBlock => Error::WouldBlock, |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | impl<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 ] |
| 268 | macro_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 | } |
| 284 | |