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
//! Ndless-specific integration with `async`/`await`
//!
//! This crate provides an executor, reactor, and utilities to use Rust's
//! `async` capabilities with the TI Nspire's timer and keypad. Note that
//! normally `async` functions are used for I/O. However, as far as I'm aware,
//! the TI-Nspire's OS has no support for asynchronous I/O of any sort. However,
//! this still provides helpful utilities for doing multiple things at once,
//! such as waiting for a key with a timeout.
//!
//! You'll first need to create an instance of
//! [`AsyncListeners`][task::AsyncListeners] with `AsyncListeners::default()`.
//! This allows you to receive events from the Nspire's timer. From there, you
//! can pass it into [`task::block_on`], along with a `Future` of your choice.
//!
//! # Helpful resources
//! Check out the [Rust Async Book](https://rust-lang.github.io/async-book/).
//! This has useful instructions about asynchronous programming. Although it is
//! mostly written for usage with a full operating system, everything applies
//! here except chapters 1.4, 6.3, 8, and 10.
//!
//! [`futures_util`] has many useful utility functions. Add it to your project
//! by adding the following to your Cargo.toml:
//!
//! ```toml
//! futures-util = { version = "0.3.5", default-features = false, features = ["alloc", "async-await-macro"] }
//! ```
//!
//! You may find its
//! [`FuturesUnordered`](https://docs.rs/futures-util/0.3.*/futures_util/stream/futures_unordered/struct.FuturesUnordered.html)
//! to be of help for scheduling multiple tasks. Although
//! macros like [`join`] and [`first`] can be helpful, they aren't as efficient
//! and flexible as it.
//!
//! The macros [`join`], [`select`], [`try_join`], and traits [`FutureExt`] &
//! [`StreamExt`] are re-exported from it, so if that's all you need, you don't
//! need to depend on it directly.
//!
//! # Example
//! ```rust
//! use futures_util::future;
//! use ndless_async::task::{block_on, AsyncListeners};
//! use ndless_async::{first, StreamExt};
//! use ndless_async::keypad::KeypadListener;
//! use ndless::input::Key;
//!
//! let listeners = AsyncListeners::new();
//! let keypad = KeypadListener::new(&listeners.timer());
//! block_on(&listeners, async {
//!     let _ = listeners.timer().timeout_ms(5000, do_stuff(&keypad)).await;
//!     listeners.timer().sleep_ms(2000).await;
//!     first!(do_other_stuff(&listeners), wait_for_esc(&keypad));
//! });
//!
//! async fn wait_for_esc(keypad: &KeypadListener<'_>) {
//!     keypad
//!         .stream()
//!         .filter(|key| future::ready(key.key == Key::Esc))
//!         .next()
//!         .await;
//! }
//!
//! async fn do_other_stuff(listeners: &AsyncListeners) {
//!     loop {
//!         listeners.timer().sleep_ms(1000).await;
//!         println!("1s!");
//!     }
//! }
//!
//! async fn do_stuff(keypad: &KeypadListener<'_>) {
//!     use ndless_async::keypad::KeyState::*;
//!     let mut keypad = keypad.stream();
//!     while let Some(event) = keypad.next().await {
//!         println!(
//!             "Key {:?} was {}",
//!             event.key,
//!             if event.state == Released {
//!                 "released"
//!             } else {
//!                 "pressed"
//!             }
//!         );
//!         print!("Keys currently pressed: ");
//!         keypad
//!             .list_keys()
//!             .iter()
//!             .for_each(|key| print!("{:?} ", key));
//!         println!();
//!         if event.key == Key::Esc { break; }
//!     }
//! }
//! ```

#![feature(wake_trait)]
#![no_std]
extern crate alloc;

pub use futures_util::{join, select_biased as select, try_join, FutureExt, StreamExt};

pub use yield_now::Yield;

pub mod keypad;
pub mod mpsc;
pub mod task;
pub mod timer;
mod yield_now;
/// Polls for the first future to complete, and then cancels the remaining ones.
/// If you care about the return value, use [`select`]. This macro must
/// be used in an `async` context, such as an `async fn` or `async { }` block.
///
/// [`FuturesUnordered`](https://docs.rs/futures-util/0.3.*/futures_util/stream/futures_unordered/struct.FuturesUnordered.html)
/// can be more flexible and efficient than this macro when you have many
/// `Future`s or need to dynamically add and remove them.
///
/// # Example
/// The call to [`block_on`][task::block_on] completes after 5 seconds or
/// when the escape key is pressed, whichever comes first.
///
/// In reality, you should use
/// [`TimerListener::timeout`][timer::TimerListener::timeout] for this purpose.
/// However, `first!` can be used for other, more complex cases.
///
/// ```
/// use ndless_async::task::{AsyncListeners, block_on};
/// use ndless_async::StreamExt;
/// use ndless::input::Key;
///
/// let listeners = AsyncListeners::new();
/// block_on(&listeners, async { first!(timeout(&listeners), listen_for_esc(&listeners)) });
///
///
/// async fn timeout(listeners: &AsyncListeners) {
///     listeners.timer().sleep_ms(5000).await;
/// }
///
/// async fn listen_for_esc(listeners: &AsyncListeners) {
///     let mut keypad = listeners.keypad();
///     while let Some(event) = keypad.next().await {
///         if event.key == Key::Esc {
///             break;
///         }
///     }
/// }
/// ```
#[macro_export]
macro_rules! first {
	($( $arg:expr ),*) => (
		$crate::select!($(_ = $crate::FutureExt::fuse($arg) => (),)*)
	)
}