Skip to content

Commit 343e20d

Browse files
committed
init
0 parents  commit 343e20d

File tree

7 files changed

+275
-0
lines changed

7 files changed

+275
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/target
2+
**/*.rs.bk
3+
Cargo.lock

.travis.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
language: rust
2+
sudo: false
3+
rust:
4+
- nightly-2019-02-19
5+
6+
os:
7+
- linux

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "futures-async-combinators"
3+
version = "0.1.0"
4+
authors = ["Roman Proskuryakov <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
9+
[dependencies.futures]
10+
version = "=0.3.0-alpha.13"
11+
package = "futures-preview"

LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Permission is hereby granted, free of charge, to any
2+
person obtaining a copy of this software and associated
3+
documentation files (the "Software"), to deal in the
4+
Software without restriction, including without
5+
limitation the rights to use, copy, modify, merge,
6+
publish, distribute, sublicense, and/or sell copies of
7+
the Software, and to permit persons to whom the Software
8+
is furnished to do so, subject to the following
9+
conditions:
10+
11+
The above copyright notice and this permission notice
12+
shall be included in all copies or substantial portions
13+
of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
16+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
17+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
18+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
19+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
22+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23+
DEALINGS IN THE SOFTWARE.NGS IN THE SOFTWARE.
24+

README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# futures-async-combinators
2+
3+
[![Build Status](https://travis-ci.com/kpp/futures-async-combinators.svg?branch=master)](https://travis-ci.com/kpp/futures-async-combinators)
4+
5+
FOR LEARNING PURPOSES ONLY
6+
7+
This is a greatly simplified implementation of [https://docs.rs/futures-preview/0.3.0-alpha.13/futures/](Future combinators)
8+
like `FutureExt::map`, `TryFutureExt::and_then`...
9+
10+
# Requirements
11+
12+
Rust nightly-2019-02-19 for async_await, await_macro...
13+
14+
# Why
15+
16+
To understand how combinators work by looking at clean source code. Compare:
17+
18+
```rust
19+
pub async fn then<...>(future, f) -> ...
20+
{
21+
let future_result = await!(future);
22+
let new_future = f(future_result);
23+
await!(new_future)
24+
}
25+
```
26+
27+
with original
28+
29+
```rust
30+
impl Then
31+
{
32+
fn poll(self, waker) -> Poll<...> {
33+
self.as_mut().chain().poll(waker, |output, f| f(output))
34+
}
35+
}
36+
37+
impl Chain
38+
{
39+
fn poll(self, waker, f) -> Poll<...>
40+
{
41+
let mut f = Some(f);
42+
43+
// Safe to call `get_unchecked_mut` because we won't move the futures.
44+
let this = unsafe { Pin::get_unchecked_mut(self) };
45+
46+
loop {
47+
let (output, data) = match this {
48+
Chain::First(fut1, data) => {
49+
match unsafe { Pin::new_unchecked(fut1) }.poll(waker) {
50+
Poll::Pending => return Poll::Pending,
51+
Poll::Ready(output) => (output, data.take().unwrap()),
52+
}
53+
}
54+
Chain::Second(fut2) => {
55+
return unsafe { Pin::new_unchecked(fut2) }.poll(waker);
56+
}
57+
Chain::Empty => unreachable!()
58+
};
59+
60+
*this = Chain::Empty; // Drop fut1
61+
let fut2 = (f.take().unwrap())(output, data);
62+
*this = Chain::Second(fut2)
63+
}
64+
}
65+
}
66+
```

src/future.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use futures::future::Future;
2+
3+
pub async fn map<Fut, U, F>(future: Fut, f: F) -> U
4+
where F: FnOnce(Fut::Output) -> U,
5+
Fut: Future,
6+
{
7+
let future_result = await!(future);
8+
f(future_result)
9+
}
10+
11+
pub async fn then<FutA, FutB, F>(future: FutA, f: F) -> FutB::Output
12+
where F: FnOnce(FutA::Output) -> FutB,
13+
FutA: Future,
14+
FutB: Future,
15+
{
16+
let future_result = await!(future);
17+
let new_future = f(future_result);
18+
await!(new_future)
19+
}
20+
21+
pub async fn and_then<FutA, FutB, F, T, U, E>(future: FutA, f: F) -> Result<U, E>
22+
where F: FnOnce(T) -> FutB,
23+
FutA: Future<Output = Result<T,E>>,
24+
FutB: Future<Output = Result<U,E>>,
25+
{
26+
let future_result = await!(future);
27+
match future_result {
28+
Ok(ok) => {
29+
let new_future = f(ok);
30+
await!(new_future)
31+
},
32+
Err(err) => Err(err),
33+
}
34+
}
35+
36+
pub async fn or_else<FutA, FutB, F, T, E, U>(future: FutA, f: F) -> Result<T, U>
37+
where F: FnOnce(E) -> FutB,
38+
FutA: Future<Output = Result<T,E>>,
39+
FutB: Future<Output = Result<T,U>>,
40+
{
41+
let future_result = await!(future);
42+
match future_result {
43+
Ok(ok) => Ok(ok),
44+
Err(err) => {
45+
let new_future = f(err);
46+
await!(new_future)
47+
},
48+
}
49+
}
50+
51+
pub async fn map_ok<Fut, F, T, U, E>(future: Fut, f: F) -> Result<U, E>
52+
where F: FnOnce(T) -> U,
53+
Fut: Future<Output = Result<T,E>>,
54+
{
55+
let future_result = await!(future);
56+
future_result.map(f)
57+
}
58+
59+
pub async fn map_err<Fut, F, T, E, U>(future: Fut, f: F) -> Result<T, U>
60+
where F: FnOnce(E) -> U,
61+
Fut: Future<Output = Result<T,E>>,
62+
{
63+
let future_result = await!(future);
64+
future_result.map_err(f)
65+
}
66+
67+
pub async fn flatten<FutA, FutB>(future: FutA) -> FutB::Output
68+
where FutA: Future<Output = FutB>,
69+
FutB: Future,
70+
{
71+
let nested_future = await!(future);
72+
await!(nested_future)
73+
}
74+
75+
pub async fn inspect<Fut, F>(future: Fut, f: F) -> Fut::Output
76+
where Fut: Future,
77+
F: FnOnce(&Fut::Output),
78+
{
79+
let future_result = await!(future);
80+
f(&future_result);
81+
future_result
82+
}
83+
84+
85+
#[cfg(test)]
86+
mod tests {
87+
use futures::{future, executor};
88+
use crate::future::*;
89+
90+
#[test]
91+
fn test_map() {
92+
executor::block_on(async {
93+
let future = future::ready(1);
94+
let new_future = map(future, |x| x + 3);
95+
assert_eq!(await!(new_future), 4);
96+
});
97+
}
98+
99+
#[test]
100+
fn test_then() {
101+
executor::block_on(async {
102+
let future = future::ready(1);
103+
let new_future = then(future, |x| future::ready(x + 3));
104+
assert_eq!(await!(new_future), 4);
105+
});
106+
}
107+
108+
#[test]
109+
fn test_and_then() {
110+
executor::block_on(async {
111+
let future = future::ready(Ok::<i32, i32>(1));
112+
let new_future = and_then(future, |x| future::ready(Ok::<i32, i32>(x + 3)));
113+
assert_eq!(await!(new_future), Ok(4));
114+
});
115+
}
116+
117+
#[test]
118+
fn test_or_else() {
119+
executor::block_on(async {
120+
let future = future::ready(Err::<i32, i32>(1));
121+
let new_future = or_else(future, |x| future::ready(Err::<i32, i32>(x + 3)));
122+
assert_eq!(await!(new_future), Err(4));
123+
});
124+
}
125+
126+
#[test]
127+
fn test_map_ok() {
128+
executor::block_on(async {
129+
let future = future::ready(Ok::<i32, i32>(1));
130+
let new_future = map_ok(future, |x| x + 3);
131+
assert_eq!(await!(new_future), Ok(4));
132+
});
133+
}
134+
135+
#[test]
136+
fn test_map_err() {
137+
executor::block_on(async {
138+
let future = future::ready(Err::<i32, i32>(1));
139+
let new_future = map_err(future, |x| x + 3);
140+
assert_eq!(await!(new_future), Err(4));
141+
});
142+
}
143+
144+
#[test]
145+
fn test_flatten() {
146+
executor::block_on(async {
147+
let nested_future = future::ready(future::ready(1));
148+
let future = flatten(nested_future);
149+
assert_eq!(await!(future), 1);
150+
});
151+
}
152+
153+
#[test]
154+
fn test_inspect() {
155+
executor::block_on(async {
156+
let future = future::ready(1);
157+
let new_future = inspect(future, |&x| assert_eq!(x, 1));
158+
assert_eq!(await!(new_future), 1);
159+
});
160+
}
161+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#![feature(async_await, await_macro, futures_api)]
2+
3+
pub mod future;

0 commit comments

Comments
 (0)