Skip to main content

fauxgen/
lib.rs

1//! This crate implements generators for Rust.
2//!
3//! Rust has built-in generators but they are currently unstable and so they can
4//! only be used on nightly. This crate allows you to write your own generators
5//! on stable rust by using async-await underneath the hood.
6//!
7//! # Defining a Generator
8//! This crate provides two different ways to define generators. The first is as
9//! a top-level function:
10//!
11//! ```
12//! #[fauxgen::generator(yield = i32)]
13//! fn generator() {
14//!     r#yield!(1);
15//!     r#yield!(2);
16//! }
17//! ```
18//!
19//! and the second is as a lambda using the [`gen!`] macro:
20//!
21//! ```
22//! use fauxgen::{gen, GeneratorToken};
23//!
24//! let generator = fauxgen::gen!(|token: GeneratorToken<_>| {
25//!     token.yield_(1i32).await;
26//!     token.yield_(2i32).await;
27//! });
28//! ```
29//!
30//! In this case the generator uses a [`GeneratorToken`] instead of the `yield!`
31//! macro.
32//!
33//! Generators can also be async
34//! ```
35//! use std::time::Duration;
36//!
37//! #[fauxgen::generator(yield = u32)]
38//! async fn generator() {
39//!     for i in 0u32..10 {
40//!         tokio::time::sleep(Duration::from_millis(50)).await;
41//!         r#yield!(i * 2);
42//!     }
43//! }
44//! ```
45//!
46//! # Using a Generator
47//! Generators all implement either [`Generator`] or [`AsyncGenerator`]. Simple
48//! generators implement either [`Iterator`] or
49//! [`Stream`](futures_core::Stream), depending on whether the generator is
50//! async. This means that you can easily combine generators.
51//!
52//! Here we implement a generator that returns all the powers of two for a u32:
53//! ```
54//! #[fauxgen::generator(yield = u32)]
55//! fn range(max: u32) {
56//!     for i in 0..max {
57//!         r#yield!(i);
58//!     }
59//! }
60//!
61//! #[fauxgen::generator(yield = u32)]
62//! fn powers_of_two() {
63//!     for i in std::pin::pin!(range(31)) {
64//!         r#yield!(1 << i);
65//!     }
66//! }
67//! ```
68//!
69//! Note that because `fauxgen` generators are actually rust futures under the
70//! hood you will need to pin them before you can use them.
71//!
72//! # More Advanced Generator Usage
73//! Most use cases for generators will likely involve using them as iterators or
74//! streams. However, that is not all that they can do. In addition to the yield
75//! parameter, generators have both
76//! - an argument: which is the value passed in to `resume`, and,
77//! - a return value: which is the value returned when the generator completes.
78//!
79//! A complete generator that uses all of these looks like this:
80//! ```
81//! use fauxgen::{GeneratorState, Generator};
82//!
83//! #[fauxgen::generator(yield = String, arg = u32)]
84//! fn format_each() -> u64 {
85//!     let mut count = 0;
86//!     let mut value = 0;
87//!
88//!     while value < 100 {
89//!         value = r#yield!(value.to_string());
90//!         count += 1;
91//!     }
92//!
93//!     count
94//! }
95//!
96//! let mut gen = std::pin::pin!(format_each());
97//!
98//! for value in [0, 5, 10, 25, 125, 87, 31] {
99//!     match gen.as_mut().resume(value) {
100//!         GeneratorState::Yielded(text) => println!("{text}"),
101//!         GeneratorState::Complete(count) => {
102//!             println!("printed {count} items");
103//!             break;
104//!         }
105//!     }
106//! }
107//! ```
108//!
109//! This is obviously somewhat harder to use than just using the generator as an
110//! iterator but it does give you more abilities to use.
111//!
112//! ## Accessing the first argument
113//! If you run the code above (or look closely) then you might notice that the
114//! first argument passed into the generator is ignored. This usually isn't
115//! what you want. In order to access the first argument you can use the
116//! `argument!` macro:
117//!
118//! ```
119//! #[fauxgen::generator(yield = String, arg = u32)]
120//! fn format_each() -> u32 {
121//!     let mut count = 0;
122//!     let mut value = argument!();
123//!
124//!     while value < 100 {
125//!         value = r#yield!(value.to_string());
126//!         count += 1;
127//!     }
128//!
129//!     count
130//! }
131//! ```
132//!
133//! Note that using the `argument!` macro after you have called `yield!` is
134//! likely to result in a panic.
135
136#![cfg_attr(std_generators, feature(generator_trait))]
137
138extern crate self as fauxgen;
139
140// A small helper macro to avoid unused_imports warnings for items that have
141// only been pulled in to scope so they can be referred to in docs.
142macro_rules! used_in_docs {
143    ($( $name:ident ),+ $(,)? ) => {
144        const _: () = {
145            #[allow(unused_imports)]
146            mod _used_in_docs {
147                $( use super::$name; )+
148            }
149        };
150    };
151}
152
153#[path = "async.rs"]
154mod asynk;
155mod detail;
156mod export;
157mod impls;
158mod iter;
159mod stream;
160mod token;
161
162#[cfg(not(std_generators))]
163mod core;
164
165#[cfg(std_generators)]
166mod core {
167    pub use std::ops::{Generator, GeneratorState};
168}
169
170#[doc = include_str!("../README.md")]
171mod readme {}
172
173/// Declare a standalone generator function.
174///
175/// This attribute macro transforms a function definition into a generator
176/// definition.
177///
178/// # Parameters
179/// - `yield` - The type that will be yielded from the generator. If not
180///   specified then `()` will be used instead.
181/// - `arg` - The type of the argument that will be passed to the generator via
182///   `resume`. This can be accessed via the `argument!` and `r#yield!` macros
183///   within the generator.
184/// - `crate` - A path at which the fauxgen crate can be accessed. If not
185///   specified then it will use `::fauxgen`.
186///
187/// # Interface
188/// This attribute macro creates two regular macros that can only be used inside
189/// the generator definition itself:
190/// ## `r#yield!`
191/// This is the equivalent of the yield keyword. It takes a value to yield to
192/// the caller and returns the argument that was passed in on resume.
193///
194/// ```
195/// use fauxgen::{Generator, GeneratorState};
196///
197/// #[fauxgen::generator(yield = &'static str, arg = &'static str)]
198/// fn example() -> &'static str {
199///     r#yield!("test")
200/// }
201///
202/// let mut gen = std::pin::pin!(example());
203/// assert!(matches!(gen.as_mut().resume("ignored"), GeneratorState::Yielded("test")));
204/// assert!(matches!(gen.as_mut().resume("another"), GeneratorState::Complete("another")));
205/// ```
206///
207/// Note, however, that the first argument passed into the generator is ignored.
208/// In order to extract the first argument we need to use the `argument!` macro.
209///
210/// ## `argument!`
211/// This macro extracts the argument passed to the very first resume call, the
212/// one that started the generator. It is only valid to call before the first
213/// yield, doing so after will result in a panic.
214///
215/// ```
216/// #[fauxgen::generator(arg = &'static str)]
217/// fn example() {
218///     let first = argument!();
219///     let second = r#yield!();
220///     let third = r#yield!();
221/// }
222/// ```
223///
224/// # Using the `yield` keyword
225/// This macro supports using the `yield` keyword in place of the `r#yield!`
226/// macro. Note that the keyword itself is unstable in rust and to just use it
227/// you will need to enable the nightly `generators` feature.
228#[cfg_attr(nightly, doc = "```")]
229#[cfg_attr(not(nightly), doc = "```ignore")]
230/// #![feature(coroutines)]
231///
232/// #[fauxgen::generator(yield = &'static str)]
233/// fn generator() {
234///     yield "first";
235///     yield "second";
236///     yield "third";
237/// }
238/// ```
239#[cfg(feature = "macros")]
240pub use fauxgen_macros::generator;
241
242pub use crate::asynk::{AsyncGenerator, Resume};
243pub use crate::core::{Generator, GeneratorState};
244pub use crate::iter::GeneratorIter;
245pub use crate::stream::{GeneratorStream, GeneratorTryStream};
246pub use crate::token::GeneratorToken;
247
248/// Declare an inline generator function.
249///
250/// This is a declarative version of the [`generator`] macro. It can be used to
251/// declare a generator inline without giving it a named function.
252///
253/// Unlike with the [`generator`] macro, this generator type instead takes in a
254/// [`GeneratorToken`] which is used to yield values and to access generator
255/// arguments.
256///
257/// # Example
258/// The simplest type of generator is one which only yields values:
259/// ```
260/// use fauxgen::{gen, GeneratorToken};
261///
262/// let gen = gen!(|token: GeneratorToken<_>| {
263///     token.yield_(5i32).await;
264///     token.yield_(6).await;
265///     token.yield_(77).await;
266/// });
267/// let gen = std::pin::pin!(gen);
268///
269/// let vals: Vec<i32> = gen.collect();
270/// assert_eq!(vals, [5, 6, 77]);
271/// ```
272///
273/// # Interface
274/// The interface exposed here is similar, in principle, to that offered by the
275/// [`generator`] macro. You can await upon the methods exposed by the
276/// [`GeneratorToken`] in order to both yield a value as well as to get the
277/// first generator argument.
278///
279/// See the documentation of the [`generator`] macro for a description of what
280/// each one does.
281///
282/// # Restrictions
283/// This macro allows you to use await within the generator. However, it is an
284/// error to do this unless the generator is async. Awaiting on a future other
285/// than those gotten by calling methods on the [`GeneratorToken`] will result
286/// in a panic for sync generators.
287#[cfg(doc)]
288#[macro_export]
289macro_rules! gen {
290    (async $(move)? $func:expr) => {};
291    (      $(move)? $func:expr) => {};
292}
293
294/// Declare an inline generator function.
295#[cfg(not(doc))]
296#[macro_export]
297macro_rules! gen {
298    (async $(move $($dummy:tt)?)? |$token:ident$( : $ty:ty)?| $body:expr) => {{
299        let func = $(move $($dummy)?)? |$token $( : $ty)?| async move { $body };
300        $crate::gen_impl!(gen_async => func)
301    }};
302    ($(move $($dummy:tt)?)? |$token:ident$( : $ty:ty)?| $body:expr) => {{
303        let func = $(move $($dummy)?)? |$token $( : $ty)?| async move { $body };
304        $crate::gen_impl!(gen_sync => func)
305    }};
306}
307
308#[macro_export]
309#[doc(hidden)]
310macro_rules! gen_impl {
311    ($genfn:ident => $func:expr) => {{
312        let token = $crate::__private::token();
313
314        $crate::__private::$genfn(token.marker(), async move {
315            let token: $crate::GeneratorToken<_, _> =
316                $crate::__private::register_owned(token).await;
317            $func(token).await
318        })
319    }};
320}
321
322#[doc(hidden)]
323pub mod __private {
324    use std::pin::Pin;
325
326    use crate::GeneratorToken;
327
328    // separate exports ..
329    #[allow(dead_code)]
330    fn _dummy() {}
331
332    pub use std::future::Future;
333    pub use std::pin::pin;
334
335    pub use crate::detail::{RawGeneratorToken, TokenMarker};
336    pub use crate::export::{AsyncGenerator, SyncGenerator};
337
338    pub fn gen_sync<F, Y, A>(_: TokenMarker<Y, A>, future: F) -> SyncGenerator<F, Y, A> {
339        SyncGenerator::new(future)
340    }
341
342    pub fn gen_async<F, Y, A>(_: TokenMarker<Y, A>, future: F) -> AsyncGenerator<F, Y, A> {
343        AsyncGenerator::new(future)
344    }
345
346    pub fn token<Y, A>() -> RawGeneratorToken<Y, A> {
347        RawGeneratorToken::new()
348    }
349
350    pub async fn register<Y, A>(token: Pin<&RawGeneratorToken<Y, A>>) {
351        // SAFETY: register is only called from the prelude generated by the
352        //         #[generator] macro. The macro takes responsibility for ensuring that
353        //         the parameters match.
354        unsafe { token.register().await }
355    }
356
357    pub async fn register_owned<Y, A>(token: RawGeneratorToken<Y, A>) -> GeneratorToken<Y, A> {
358        // SAFETY: register_owned is only called from the code emitted by the gen!
359        //         macro. The macro takes responsibility for ensuring that the
360        //         parameters match.
361        unsafe { GeneratorToken::register(token).await }
362    }
363}