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}