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


#![no_std]

#![deny(missing_docs)]


pub use embedded_hal;
pub use nb;


/// Blocks on a non-blocking operation until a timer times out
///
/// Expects two arguments:
///
/// - A timer that implements `embedded_hal::timer::CountDown`
/// - An expression that evaluates to `nb::Result<T, E>`
///
/// Evaluates the expression and returns `Result<T, TimeoutError<E>>`.
///
/// # Example
///
/// ``` rust
/// use embedded_timeout_macros::{
///     block_timeout,
///     TimeoutError,
/// };
/// #
/// # struct Timer;
/// #
/// # impl embedded_hal::timer::CountDown for Timer {
/// #     type Time = ();
/// #     fn start<T>(&mut self, _: T) {}
/// #     fn wait(&mut self) -> nb::Result<(), void::Void> { Ok(()) }
/// # }
/// #
/// # let mut timer = Timer;
///
/// let result: Result<(), TimeoutError<()>> = block_timeout!(
///     &mut timer,
///     {
///         // The macro will keep evaluation this expression repeatedly until
///         // it returns `Ok` or until the timer times out.
///         //
///         // We can do anything that returns `nb::Result` here. For this
///         // simple example, we just return `Ok`.
///         Ok(())
///     }
/// );
///
/// match result {
///     Ok(()) => {
///         // success
///     }
///     Err(TimeoutError::Timeout) => {
///         // the operation timed out
///     }
///     Err(TimeoutError::Other(error)) => {
///         // the operation returned another error
///     }
/// }
/// ```
#[macro_export]
macro_rules! block_timeout {
    ($timer:expr, $op:expr) => {
        {
            use $crate::embedded_hal::prelude::*;

            // Make sure the timer has the right type. If it hasn't, the user
            // should at least get a good error message.
            fn check_type<T>(_: &mut T)
                where T: $crate::embedded_hal::timer::CountDown {}
            check_type($timer);

            loop {
                match $timer.wait() {
                    Ok(()) =>
                        break Err($crate::TimeoutError::Timeout),
                    Err($crate::nb::Error::WouldBlock) =>
                        (),
                    Err(_) =>
                        unreachable!(),
                }

                match $op {
                    Ok(result) =>
                        break Ok(result),
                    Err($crate::nb::Error::WouldBlock) =>
                        (),
                    Err($crate::nb::Error::Other(error)) =>
                        break Err($crate::TimeoutError::Other(error)),
                }
            }
        }
    }
}

/// Repeats an operation until a timer times out
///
/// Expects four arguments:
///
/// - A timer that implements `embedded_hal::timer::CountDown`
/// - An expression that evaluates to `Result<T, E>` (the operation)
/// - A pseudo-closure that will be called every time the operation succeeds
///   This pseudo-closure is expected to take an argument of type `T`. The
///   return value is ignored.
/// - A pseudo-closure that will be called every time the operation fails
///   This pseudo-closure is expected to take an argument of type `E`. The
///   return value is ignored.
///
/// `repeat_timeout!` will keep repeating the operation until the timer runs
/// out, no matter whether it suceeds or fails.
///
/// It uses a `loop` to do that, which is `break`s from when the timer runs out.
/// Any of the expressions passed into the macro, the main expression, as well
/// as the two pseudo-closures, can employ `break` and `continue` to manipulate
/// that loop.
///
/// # Example
///
/// ``` rust
/// use embedded_timeout_macros::{
///     repeat_timeout,
///     TimeoutError,
/// };
/// #
/// # struct Timer;
/// #
/// # impl embedded_hal::timer::CountDown for Timer {
/// #     type Time = ();
/// #     fn start<T>(&mut self, _: T) {}
/// #     fn wait(&mut self) -> nb::Result<(), void::Void> { Ok(()) }
/// # }
/// #
/// # let mut timer = Timer;
///
/// repeat_timeout!(
///     &mut timer,
///     {
///         // The macro will keep evaluating this expression repeatedly until
///         // the timer times out.
///         //
///         // We can do anything that returns `Result` here. For this simple
///         // example, we just return `Ok`.
///         Ok(())
///
///         // We could also return an error.
///         // Err("This is an error")
///     },
///     // Here's a pseudo-closure with an argument in parentheses, which we can
///     // name freely, followed by an expression whose return value is ignored.
///     (result) {
///         // The macro will evaluate this expression, if the main expression
///         // above returns `Ok`. `result`, which we've named in the
///         // parentheses above, will be whatever the contents of the `Ok` are.
///         let result: () = result;
///     };
///     (error) {
///         // will be called by the macro, if the expression returns `Err`
///         let error: &'static str = error;
///     };
/// );
/// ```
#[macro_export]
macro_rules! repeat_timeout {
    (
        $timer:expr,
        $op:expr,
        ($result:ident) $on_success:expr;
        ($error:ident) $on_error:expr;
    ) => {
        {
            use $crate::embedded_hal::prelude::*;

            // Make sure the timer has the right type. If it hasn't, the user
            // should at least get a good error message.
            fn check_type<T>(_: &mut T)
                where T: $crate::embedded_hal::timer::CountDown {}
            check_type($timer);

            loop {
                match $timer.wait() {
                    Ok(()) =>
                        break,
                    Err($crate::nb::Error::WouldBlock) =>
                        (),
                    Err(_) =>
                        unreachable!(),
                }

                match $op {
                    Ok(result) => {
                        let $result = result;
                        $on_success;
                    }
                    Err(error) => {
                        let $error = error;
                        $on_error;
                    }
                }
            }
        }
    }
}


/// An error that can either be a timeout or another error
///
/// Returned by the [`block_timeout`] macro.
#[derive(Debug)]
pub enum TimeoutError<T> {
    /// The operation timed out
    Timeout,

    /// Another error occured
    Other(T),
}