ndless_async/
lib.rs

1//! Ndless-specific integration with `async`/`await`
2//!
3//! This crate provides an executor, reactor, and utilities to use Rust's
4//! `async` capabilities with the TI Nspire's timer and keypad. Note that
5//! normally `async` functions are used for I/O. However, as far as I'm aware,
6//! the TI-Nspire's OS has no support for asynchronous I/O of any sort. However,
7//! this still provides helpful utilities for doing multiple things at once,
8//! such as waiting for a key with a timeout.
9//!
10//! You'll first need to create an instance of
11//! [`AsyncListeners`][task::AsyncListeners] with `AsyncListeners::default()`.
12//! This allows you to receive events from the Nspire's timer. From there, you
13//! can pass it into [`task::block_on`], along with a `Future` of your choice.
14//!
15//! # Helpful resources
16//! Check out the [Rust Async Book](https://rust-lang.github.io/async-book/).
17//! This has useful instructions about asynchronous programming. Although it is
18//! mostly written for usage with a full operating system, everything applies
19//! here except chapters 1.4, 6.3, 8, and 10.
20//!
21//! [`futures_util`] has many useful utility functions. Add it to your project
22//! by adding the following to your Cargo.toml:
23//!
24//! ```toml
25//! futures-util = { version = "0.3.5", default-features = false, features = ["alloc", "async-await-macro"] }
26//! ```
27//!
28//! You may find its
29//! [`FuturesUnordered`](https://docs.rs/futures-util/0.3.*/futures_util/stream/futures_unordered/struct.FuturesUnordered.html)
30//! to be of help for scheduling multiple tasks. Although
31//! macros like [`join`] and [`first`] can be helpful, they aren't as efficient
32//! and flexible as it.
33//!
34//! The macros [`join`], [`select`], [`try_join`], and traits [`FutureExt`] &
35//! [`StreamExt`] are re-exported from it, so if that's all you need, you don't
36//! need to depend on it directly.
37//!
38//! # Example
39//! ```rust
40//! use futures_util::future;
41//! use ndless_async::task::{block_on, AsyncListeners};
42//! use ndless_async::{first, StreamExt};
43//! use ndless_async::keypad::KeypadListener;
44//! use ndless::input::Key;
45//!
46//! let listeners = AsyncListeners::new();
47//! let keypad = KeypadListener::new(&listeners.timer());
48//! block_on(&listeners, async {
49//!     let _ = listeners.timer().timeout_ms(5000, do_stuff(&keypad)).await;
50//!     listeners.timer().sleep_ms(2000).await;
51//!     first!(do_other_stuff(&listeners), wait_for_esc(&keypad));
52//! });
53//!
54//! async fn wait_for_esc(keypad: &KeypadListener<'_>) {
55//!     keypad
56//!         .stream()
57//!         .filter(|key| future::ready(key.key == Key::Esc))
58//!         .next()
59//!         .await;
60//! }
61//!
62//! async fn do_other_stuff(listeners: &AsyncListeners) {
63//!     loop {
64//!         listeners.timer().sleep_ms(1000).await;
65//!         println!("1s!");
66//!     }
67//! }
68//!
69//! async fn do_stuff(keypad: &KeypadListener<'_>) {
70//!     use ndless_async::keypad::KeyState::*;
71//!     let mut keypad = keypad.stream();
72//!     while let Some(event) = keypad.next().await {
73//!         println!(
74//!             "Key {:?} was {}",
75//!             event.key,
76//!             if event.state == Released {
77//!                 "released"
78//!             } else {
79//!                 "pressed"
80//!             }
81//!         );
82//!         print!("Keys currently pressed: ");
83//!         keypad
84//!             .list_keys()
85//!             .iter()
86//!             .for_each(|key| print!("{:?} ", key));
87//!         println!();
88//!         if event.key == Key::Esc { break; }
89//!     }
90//! }
91//! ```
92
93#![feature(wake_trait)]
94#![no_std]
95extern crate alloc;
96
97pub use futures_util::{join, select_biased as select, try_join, FutureExt, StreamExt};
98
99pub use yield_now::Yield;
100
101pub mod keypad;
102pub mod mpsc;
103pub mod task;
104pub mod timer;
105mod yield_now;
106/// Polls for the first future to complete, and then cancels the remaining ones.
107/// If you care about the return value, use [`select`]. This macro must
108/// be used in an `async` context, such as an `async fn` or `async { }` block.
109///
110/// [`FuturesUnordered`](https://docs.rs/futures-util/0.3.*/futures_util/stream/futures_unordered/struct.FuturesUnordered.html)
111/// can be more flexible and efficient than this macro when you have many
112/// `Future`s or need to dynamically add and remove them.
113///
114/// # Example
115/// The call to [`block_on`][task::block_on] completes after 5 seconds or
116/// when the escape key is pressed, whichever comes first.
117///
118/// In reality, you should use
119/// [`TimerListener::timeout`][timer::TimerListener::timeout] for this purpose.
120/// However, `first!` can be used for other, more complex cases.
121///
122/// ```
123/// use ndless_async::task::{AsyncListeners, block_on};
124/// use ndless_async::StreamExt;
125/// use ndless::input::Key;
126///
127/// let listeners = AsyncListeners::new();
128/// block_on(&listeners, async { first!(timeout(&listeners), listen_for_esc(&listeners)) });
129///
130///
131/// async fn timeout(listeners: &AsyncListeners) {
132///     listeners.timer().sleep_ms(5000).await;
133/// }
134///
135/// async fn listen_for_esc(listeners: &AsyncListeners) {
136///     let mut keypad = listeners.keypad();
137///     while let Some(event) = keypad.next().await {
138///         if event.key == Key::Esc {
139///             break;
140///         }
141///     }
142/// }
143/// ```
144#[macro_export]
145macro_rules! first {
146	($( $arg:expr ),*) => (
147		$crate::select!($(_ = $crate::FutureExt::fuse($arg) => (),)*)
148	)
149}