embedded_timeout_macros/lib.rs
1//! Useful macros for working with timeouts on top of `embedded-hal` APIs
2//!
3//! The non-blocking APIs in the [`embedded-hal`] crate use `nb::Result` from
4//! the [`nb`] crate to signal whether an operation has finished. This can be
5//! a bit tedious to work with in non-trivial cases, for example if timeouts are
6//! involved.
7//!
8//! This crate defines macros that help working with timeouts on top of
9//! `embedded-hal` APIs.
10//!
11//! # Why Macros?
12//!
13//! A similar result could be achieved using functions and closures, so "Why use
14//! macros for this?" is a fair question. While macros can be confusing and hard
15//! to work with, they are also more powerful. Here are some things you can do
16//! using the macros in this crate, that you couldn't do with a
17//! function/closure-based approach:
18//!
19//! - You can `return` the current function, from within the closure.
20//! - You can `break`/`continue` an outer loop, from within the closure.
21//! - You can rely on the compiler's reasoning to support complicated moves.
22//!
23//! That last point is the least obvious, so it deserves some elaboration. Take
24//! the following example:
25//!
26//! ``` no_run
27//! let mut thing_being_idle = Thing::new();
28//!
29//! loop {
30//! // `do_stuff` takes ownership of the idle thing and returns a new type
31//! // that represents the same thing, but no longer being idle and doing
32//! // stuff.
33//! let thing_doing_stuff = thing_being_idle.do_stuff();
34//!
35//! // stuff is being done
36//!
37//! // `finish_doing_stuff` again takes ownership of the thing, and returns
38//! // the original type that represents the thing while it's being idle.
39//! // We move that new idle thing into the original variable.
40//! //
41//! // The compiler understands this, so even though we've moved out of
42//! // `thing_being_idle`, it's going to be available again in the next loop
43//! // iteration.
44//! thing_being_idle = thing_doing_stuff.finish_doing_stuff();
45//! }
46//!
47//!
48//! struct Thing<State>(State);
49//!
50//! impl Thing<BeingIdle> {
51//! fn new() -> Self {
52//! Thing(BeingIdle)
53//! }
54//!
55//! fn do_stuff(self) -> Thing<DoingStuff> {
56//! // Start doing the important stuff
57//! // ...
58//!
59//! Thing(DoingStuff)
60//! }
61//! }
62//!
63//! impl Thing<DoingStuff> {
64//! fn finish_doing_stuff(self) -> Thing<BeingIdle> {
65//! // Finish doing the important stuff
66//! // ...
67//!
68//! Thing(BeingIdle)
69//! }
70//! }
71//!
72//! struct BeingIdle;
73//! struct DoingStuff;
74//! ```
75//!
76//! Since the macros in this crate are basically just fancy loops that don't do
77//! anything complicated, the principle demonstrated above fully applies when
78//! using them.
79//!
80//! Contrast that with a closure-based approach:
81//!
82//! ``` ignore
83//! let mut thing_being_idle = Thing::new();
84//!
85//! loop {
86//! let closure = || {
87//! // Since `do_stuff` takes ownership of the idle thing, the whole
88//! // closure takes ownership. We'll actually get a compiler error
89//! // here, as the compiler doesn't really understand that the closure
90//! // also gives this ownership back. See comment below.
91//! let thing_doing_stuff = thing_being_idle.do_stuff();
92//!
93//! // stuff is being done
94//!
95//! // Like in the example above, we try to give ownership back, so we
96//! // can use the variable again in the next loop iteration. However,
97//! // the compiler doesn't seem to have a concept of closures giving
98//! // back ownership, so it won't understand this, and the whole
99//! // example will not compile.
100//! thing_being_idle = thing_doing_stuff.finish_doing_stuff();
101//! };
102//!
103//! closure();
104//! }
105//!
106//!
107//! # struct Thing<State>(State);
108//! #
109//! # impl Thing<BeingIdle> {
110//! # fn new() -> Self {
111//! # Thing(BeingIdle)
112//! # }
113//! #
114//! # fn do_stuff(self) -> Thing<DoingStuff> {
115//! # // Start doing the important stuff
116//! # // ...
117//! #
118//! # Thing(DoingStuff)
119//! # }
120//! # }
121//! #
122//! # impl Thing<DoingStuff> {
123//! # fn finish_doing_stuff(self) -> Thing<BeingIdle> {
124//! # // Finish doing the important stuff
125//! # // ...
126//! #
127//! # Thing(BeingIdle)
128//! # }
129//! # }
130//! #
131//! # struct BeingIdle;
132//! # struct DoingStuff;
133//! ```
134//!
135//! [`embedded-hal`]: https://crates.io/crates/embedded-hal
136//! [`nb`]: https://crates.io/crates/nb
137
138
139#![no_std]
140
141#![deny(missing_docs)]
142
143
144pub use embedded_hal;
145pub use nb;
146
147
148/// Blocks on a non-blocking operation until a timer times out
149///
150/// Expects two arguments:
151///
152/// - A timer that implements `embedded_hal::timer::CountDown`
153/// - An expression that evaluates to `nb::Result<T, E>`
154///
155/// Evaluates the expression and returns `Result<T, TimeoutError<E>>`.
156///
157/// # Example
158///
159/// ``` rust
160/// use embedded_timeout_macros::{
161/// block_timeout,
162/// TimeoutError,
163/// };
164/// #
165/// # struct Timer;
166/// #
167/// # impl embedded_hal::timer::CountDown for Timer {
168/// # type Time = ();
169/// # fn start<T>(&mut self, _: T) {}
170/// # fn wait(&mut self) -> nb::Result<(), void::Void> { Ok(()) }
171/// # }
172/// #
173/// # let mut timer = Timer;
174///
175/// let result: Result<(), TimeoutError<()>> = block_timeout!(
176/// &mut timer,
177/// {
178/// // The macro will keep evaluation this expression repeatedly until
179/// // it returns `Ok` or until the timer times out.
180/// //
181/// // We can do anything that returns `nb::Result` here. For this
182/// // simple example, we just return `Ok`.
183/// Ok(())
184/// }
185/// );
186///
187/// match result {
188/// Ok(()) => {
189/// // success
190/// }
191/// Err(TimeoutError::Timeout) => {
192/// // the operation timed out
193/// }
194/// Err(TimeoutError::Other(error)) => {
195/// // the operation returned another error
196/// }
197/// }
198/// ```
199#[macro_export]
200macro_rules! block_timeout {
201 ($timer:expr, $op:expr) => {
202 {
203 use $crate::embedded_hal::prelude::*;
204
205 // Make sure the timer has the right type. If it hasn't, the user
206 // should at least get a good error message.
207 fn check_type<T>(_: &mut T)
208 where T: $crate::embedded_hal::timer::CountDown {}
209 check_type($timer);
210
211 loop {
212 match $timer.wait() {
213 Ok(()) =>
214 break Err($crate::TimeoutError::Timeout),
215 Err($crate::nb::Error::WouldBlock) =>
216 (),
217 Err(_) =>
218 unreachable!(),
219 }
220
221 match $op {
222 Ok(result) =>
223 break Ok(result),
224 Err($crate::nb::Error::WouldBlock) =>
225 (),
226 Err($crate::nb::Error::Other(error)) =>
227 break Err($crate::TimeoutError::Other(error)),
228 }
229 }
230 }
231 }
232}
233
234/// Repeats an operation until a timer times out
235///
236/// Expects four arguments:
237///
238/// - A timer that implements `embedded_hal::timer::CountDown`
239/// - An expression that evaluates to `Result<T, E>` (the operation)
240/// - A pseudo-closure that will be called every time the operation succeeds
241/// This pseudo-closure is expected to take an argument of type `T`. The
242/// return value is ignored.
243/// - A pseudo-closure that will be called every time the operation fails
244/// This pseudo-closure is expected to take an argument of type `E`. The
245/// return value is ignored.
246///
247/// `repeat_timeout!` will keep repeating the operation until the timer runs
248/// out, no matter whether it suceeds or fails.
249///
250/// It uses a `loop` to do that, which is `break`s from when the timer runs out.
251/// Any of the expressions passed into the macro, the main expression, as well
252/// as the two pseudo-closures, can employ `break` and `continue` to manipulate
253/// that loop.
254///
255/// # Example
256///
257/// ``` rust
258/// use embedded_timeout_macros::{
259/// repeat_timeout,
260/// TimeoutError,
261/// };
262/// #
263/// # struct Timer;
264/// #
265/// # impl embedded_hal::timer::CountDown for Timer {
266/// # type Time = ();
267/// # fn start<T>(&mut self, _: T) {}
268/// # fn wait(&mut self) -> nb::Result<(), void::Void> { Ok(()) }
269/// # }
270/// #
271/// # let mut timer = Timer;
272///
273/// repeat_timeout!(
274/// &mut timer,
275/// {
276/// // The macro will keep evaluating this expression repeatedly until
277/// // the timer times out.
278/// //
279/// // We can do anything that returns `Result` here. For this simple
280/// // example, we just return `Ok`.
281/// Ok(())
282///
283/// // We could also return an error.
284/// // Err("This is an error")
285/// },
286/// // Here's a pseudo-closure with an argument in parentheses, which we can
287/// // name freely, followed by an expression whose return value is ignored.
288/// (result) {
289/// // The macro will evaluate this expression, if the main expression
290/// // above returns `Ok`. `result`, which we've named in the
291/// // parentheses above, will be whatever the contents of the `Ok` are.
292/// let result: () = result;
293/// };
294/// (error) {
295/// // will be called by the macro, if the expression returns `Err`
296/// let error: &'static str = error;
297/// };
298/// );
299/// ```
300#[macro_export]
301macro_rules! repeat_timeout {
302 (
303 $timer:expr,
304 $op:expr,
305 ($result:ident) $on_success:expr;
306 ($error:ident) $on_error:expr;
307 ) => {
308 {
309 use $crate::embedded_hal::prelude::*;
310
311 // Make sure the timer has the right type. If it hasn't, the user
312 // should at least get a good error message.
313 fn check_type<T>(_: &mut T)
314 where T: $crate::embedded_hal::timer::CountDown {}
315 check_type($timer);
316
317 loop {
318 match $timer.wait() {
319 Ok(()) =>
320 break,
321 Err($crate::nb::Error::WouldBlock) =>
322 (),
323 Err(_) =>
324 unreachable!(),
325 }
326
327 match $op {
328 Ok(result) => {
329 let $result = result;
330 $on_success;
331 }
332 Err(error) => {
333 let $error = error;
334 $on_error;
335 }
336 }
337 }
338 }
339 }
340}
341
342
343/// An error that can either be a timeout or another error
344///
345/// Returned by the [`block_timeout`] macro.
346#[derive(Debug)]
347pub enum TimeoutError<T> {
348 /// The operation timed out
349 Timeout,
350
351 /// Another error occured
352 Other(T),
353}