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
//! ## About
//! `pecs` is a plugin for [Bevy](https://bevyengine.org) that allows you to execute code asynchronously
//! by chaining multiple promises as part of Bevy's `ecs` environment.
//!
//! `pecs` stands for `Promise Entity Component System`.
//!
//! ### Features
//!
//! - Promise chaining with [`then()`][core::PromiseLikeBase::then]/
//! [`then_repeat()`][core::PromiseLike::then_repeat]
//! - State passing (`state` for promises is like `self` for items).
//! - Complete type inference (the next promise knows the type of the previous result).
//! - Out-of-the-box timer, UI and HTTP promises via stateless [`asyn`][mod@prelude::asyn] mod and
//! stateful [`state.asyn()`][core::PromiseState::asyn] method.
//! - Custom promise registration (add any asynchronous function you want!).
//! - [System parameters](https://docs.rs/bevy/latest/bevy/ecs/system/trait.SystemParam.html) fetching
//! (promise `asyn!` functions accept the same parameters as Bevy systems do).
//! - Nested promises (with chaining, obviously).
//! - Combining promises with `any/all` for tuple/vec of promises via stateless [`any()`][core::Promise::any]
//! /[`all()`][core::Promise::all()] methods or stateful
//! [`state.any()`][core::PromiseState::any]/[`state.all()`][core::PromiseState::all] methods.
//! - State mapping via [`with(value)`][core::PromiseLikeBase::with]/
//! [`map(func)`][core::PromiseLikeBase::map]
//! (changes state type over chain calls).
//! - Result mapping via [`with_result(value)`][core::PromiseLikeBase::with_result]/
//! [`map_result(func)`][core::PromiseLikeBase::map_result] (changes result type over
//! chain calls).
//!
//! ## Example
//! ```rust
//! use bevy::prelude::*;
//! use pecs::prelude::*;
//! fn main() {
//! App::new()
//! .add_plugins(DefaultPlugins)
//! .add_plugins(PecsPlugin)
//! .add_systems(Startup, setup)
//! .run();
//! }
//!
//! fn setup(mut commands: Commands, time: Res<Time>) {
//! let start = time.elapsed_seconds();
//! commands
//! // create PromiseLike chainable commands
//! // with the current time as state
//! .promise(|| start)
//!
//! // will be executed right after current stage
//! .then(asyn!(state => {
//! info!("Wait a second..");
//! state.asyn().timeout(1.0)
//! }))
//!
//! // will be executed after in a second after previous call
//! .then(asyn!(state => {
//! info!("How large is is the Bevy main web page?");
//! state.asyn().http().get("https://bevyengine.org")
//! }))
//!
//! // will be executed after request completes
//! .then(asyn!(state, result => {
//! match result {
//! Ok(response) => info!("It is {} bytes!", response.bytes.len()),
//! Err(err) => info!("Ahhh... something goes wrong: {err}")
//! }
//! state.pass()
//! }))
//!
//! // will be executed right after the previous one
//! .then(asyn!(state, time: Res<Time> => {
//! let duration = time.elapsed_seconds() - state.value;
//! info!("It tooks {duration:0.2}s to do this job.");
//! info!("Exiting now");
//! asyn::app::exit()
//! }));
//! }
//! ```
//!
//! There is otput of the above example, pay some attention to time stamps:
//!
//! ```text
//! 18.667 INFO bevy_render::renderer: AdapterInfo { ... }
//! 18.835 INFO simple: Wait a second..
//! 19.842 INFO simple: How large is is the Bevy main web page?
//! 19.924 INFO simple: It is 17759 bytes!
//! 19.924 INFO simple: It tooks 1.09s to do this job.
//! 19.924 INFO simple: Exiting now
//! ```
//!
//! ## Basics
//!
//! To create a new promise, you can use one of the following methods:
//!
//! - [`Promise::start()`][prelude::Promise::start]: Creates a new
//! promise without initial state.
//! - [`Promise::new(state)`][prelude::Promise::new]: Creates a new promise
//! with the specified initial state.
//! - [`Promise::register(on_invoke, on_discard)`][prelude::Promise::register]:
//! Registers a new promise with the specified `on_invoke` and `on_discard` callbacks.
//!
//! It is also possible to create [`PromiseLike`][core::PromiseLike] promise containers
//! that act just like promises with:
//! - [`commands.promise(|| state)`][core::PromiseCommandsExtension::promise] for creating
//! `PromiseLike` from default state
//! - [`commands.promise(promise)`][core::PromiseCommandsExtension::promise] from existing
//! promise
//!
//! The resulting promise has the signature [`Promise<S, R>`][prelude::Promise], where `R`
//! is the type of the result and `S` is the type of the promise state. Note that `R` and `S`
//! must be `'static` types, so references or lifetime types are not allowed.
//!
//! Promises can be chained together using the [`then()`][core::PromiseLikeBase::then] method, which
//! takes an [`Asyn`][struct@core::Asyn] function created with the [`asyn!`][prelude::asyn!] macro. The
//! [`Asyn`][struct@core::Asyn] function takes the promise state as its first argument, and the promise
//! result as its second argument. Any additional arguments are optional and correspond to the
//! [system parameters][bevy::ecs::system::SystemParam] used in Bevy's ECS. This allows you to do
//! inside an [`Asyn`][struct@core::Asyn] function everything you can do inside a regular system, while
//! still keeping track of system parameter states.
//!
//! If the result of the [`Asyn`][struct@core::Asyn] function is
//! [`Resolve`][core::PromiseResult::Resolve], the result is passed immediately to
//! the next promise in the chain. If the result is [`Await`][core::PromiseResult::Await],
//! the next promise in the chain is resolved when the nested promise is resolved. The type of the next
//! promise's state and result arguments are inferred from the result of the previous promise:
//! ```rust
//! use bevy::prelude::*;
//! use pecs::prelude::*;
//! fn inference(mut commands: Commands) {
//! commands.add(
//! Promise::start(asyn!(_ => {
//! Promise::resolve("Hello!")
//! }))
//!
//! // _: PromiseState<()>, result: &str
//! .then(asyn!(_, result => {
//! info!("#1 resolved with {}", result);
//! Promise::resolve("Hello?")
//! }))
//! // _: PromiseState<()>, result: &str
//! .then(asyn!(_, result => {
//! info!("#2 resolved with {result}");
//! Promise::resolve(result.to_string())
//! }))
//! // ok_then used to take successfull results
//! // _: PromiseState<()>, result: String
//! .then(asyn!(_, result => {
//! info!("#3 resolved with {result}");
//! Promise::resolve(result)
//! }))
//! // asyn::timeout(d) returns Promise<(), (), ()>
//! // that resolves after after `d` seconds
//! .then(asyn!(_, result => {
//! info!("#4 resolved with {result}");
//! asyn::timeout(1.)
//! }))
//! // _: PromiseState<()>, result: ()
//! .then(asyn!(_, result => {
//! info!("#5 resolved with {result:?}");
//! Promise::resolve(())
//! }))
//! );
//! }
//! ```
//!
//! ## State
//! When working with asynchronous operations, it is often useful to carry a state along with
//! the promises in a chain. The `pecs` provides a convenient way to do this using the
//! [`PromiseState<S>`][core::PromiseState] type.
//!
//! [`PromiseState`][core::PromiseState] is a wrapper around a `'static S` value. This value
//! can be accessed and modified using `state.value`. [`PromiseState`][core::PromiseState] also
//! implements [`Deref`][`std::ops::Deref`], so in most you cases you can omit `.value`.
//!
//! To use [`PromiseState`][core::PromiseState], you don't create it directly. Instead, it is automatically passed
//! as the first argument to the [`Asyn`][struct@core::Asyn] function.
//!
//! For example, suppose you have a stateful promise that increments a counter, waits for some
//! time, and then logs the counter value. Here's how you could implement it:
//! ```rust
//! use bevy::prelude::*;
//! use pecs::prelude::*;
//!
//! fn setup(mut commands: Commands) {
//! commands
//! // create a promise with int state
//! .promise(|| 0)
//! .then(asyn!(state => {
//! state.value += 1;
//! state.asyn().timeout(1.0)
//! }))
//! .then(asyn!(state => {
//! info!("Counter value: {}", state.value);
//! }));
//! }
//! ```
//! In this example, we start with an initial state value of 0 and increment it by 1 in the first
//! promise. We then use `state.asyn().timeout()` to wait for one second before logging the final
//! state value. The asyn method returns an [`AsynOps<S>`][core::AsynOps] value, which can be used
//! to create new promises that are associated with the current state.
//!
//! [`PromiseState`][core::PromiseState] can be used with other pecs constructs like
//! [`then()`][core::PromiseLikeBase::then], [`repeat()`][core::Promise::repeat()] or
//! [`all()`][core::Promise::all] to create complex promise chains that carry stateful values.
//! Here's an example that uses [`any`][core::PromiseState::all] method to create a promise that
//! resolves when any of provided promises have resolved with current state itself:
//!
//! ```rust
//! use bevy::prelude::*;
//! use pecs::prelude::*;
//!
//! fn setup(mut commands: Commands, time: Res<Time>) {
//! let start = time.elapsed_seconds();
//! commands
//! // use `start: f32` as a state
//! .promise(|| start)
//! // state is f32 here
//! .then(asyn!(state => {
//! state.any((
//! asyn::timeout(0.4),
//! asyn::http::get("https://bevyengine.org").send()
//! ))
//! }))
//! // state is f32 as well
//! .then(asyn!(state, (timeout, response) => {
//! if timeout.is_some() {
//! info!("Bevy site is not fast enoutgh");
//! } else {
//! let status = if let Ok(response) = response.unwrap() {
//! response.status.to_string()
//! } else {
//! format!("Error")
//! };
//! info!("Bevy respond pretty fast with {status}");
//! }
//! // pass the state to the next call
//! state.pass()
//! }))
//! // it is still f32
//! .then(asyn!(state, time: Res<Time> {
//! let time_to_process = time.elapsed_seconds() - state.value;
//! info!("Done in {time_to_process:0.2}s");
//! }));
//! }
//! ```
//!
//! See [combine_vecs](https://github.com/jkb0o/pecs/blob/master/examples/combine_vecs.rs)
//! and [confirmation](https://github.com/jkb0o/pecs/blob/master/examples/confirmation.rs)
//! examples to better understand the `state` behaviour.
//!
//! ## Work in Progress
//! This crate is pretty young. API could and will change. App may crash. Some
//! promises could silently drop. Documentation is incomplete.
//!
//! But. But. Examples works like a charm. And this fact gives us a lot of hope.
//!
//! There are a lot docs planned to put here, but I believe it is better to release `something`
//! then `perfect`.
/// All you need is `use pecs::prelude::*`
pub use pecs_core as core;
pub use timer;
pub use pecs_http as http;