Skip to content

Commit 29f6bf8

Browse files
committed
first version done
Signed-off-by: Yoshua Wuyts <yoshuawuyts@gmail.com>
1 parent c50f503 commit 29f6bf8

File tree

4 files changed

+148
-91
lines changed

4 files changed

+148
-91
lines changed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "async-attributes"
3-
description = "Procedural macros for async-std."
3+
description = "Experimental language-level polyfills for Async Rust."
44
version = "0.3.0-alpha.5"
55
license = "MIT OR Apache-2.0"
66
readme = "README.md"
@@ -23,6 +23,7 @@ native = []
2323
syn = { version = "0.15.33", features = ["full"] }
2424
proc-macro2 = { version = "0.4.29", features = ["nightly"] }
2525
quote = "0.6.12"
26+
async-trait = "0.1.11"
2627

2728
[dev-dependencies]
28-
runtime-raw = { path = "../runtime-raw", version = "0.3.0-alpha.4" }
29+
async-std = "0.99.5"

README.md

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,63 @@
22
[![crates.io version][1]][2] [![build status][3]][4]
33
[![downloads][5]][6] [![docs.rs docs][7]][8]
44

5-
Procedural macros for async-std.
5+
Experimental language-level polyfills for Async Rust.
66

77
- [Documentation][8]
88
- [Crates.io][2]
99
- [Releases][releases]
1010

11+
## Examples
12+
13+
```rust
14+
use async_std::task;
15+
16+
#[async_attributes::main]
17+
async fn main() {
18+
println!("Hello, world!");
19+
}
20+
```
21+
22+
## About
23+
24+
Async Rust is a work in progress. The language has enabled us to do some
25+
fantastic things, but not everything is figured out yet. This crate exists
26+
to polyfill language-level support for async idioms before they can be part
27+
of the language.
28+
29+
A great example of this is `async fn main`, which we first introduced as
30+
part of the [`runtime`](https://docs.rs/runtime/0.3.0-alpha.7/runtime/) crate.
31+
Its premise is that if `async fn` is required for every `await` call, it
32+
makes sense to apply that even to `fn main`. Unfortunately this would
33+
require compiler support to enable, so we've provided an experimental
34+
polyfill for it in the mean time.
35+
36+
## Why isn't this crate part of async-std?
37+
38+
We want to make sure `async-std`'s surface area is stable, and only includes
39+
things that would make sense to be part of "an async version of std".
40+
Language level support is really important, but _not_ part of the standard
41+
library.
42+
43+
This has some distinct benefits: in particular it allows us to
44+
version both crates at a different pace. And as features are added to the
45+
language (or we decide they weren't a great idea after all), we can
46+
incrementally shrink the surface area of this crate.
47+
48+
The other big benefit is that it allows libraries to depend on `async-std`
49+
without needing to pull in the rather heavy `syn`, `quote`, and
50+
`proc-macro2` crates. This should help keep compilation times snappy for
51+
everyone.
52+
1153
## Installation
54+
1255
```sh
1356
$ cargo add async-macros
1457
```
1558

1659
## Safety
17-
This crate uses `unsafe` for pin projections.
60+
This crate uses ``#![deny(unsafe_code)]`` to ensure everything is implemented in
61+
100% Safe Rust.
1862

1963
## Contributing
2064
Want to join us? Check out our ["Contributing" guide][contributing] and take a
@@ -24,8 +68,12 @@ look at some of these issues:
2468
- [Issues labeled "help wanted"][help-wanted]
2569

2670
## References
27-
- https://github.com/rust-lang-nursery/futures-rs - the `join` + `try_join`
28-
macros are direct ports of the old `macro_rules` impls from `futures-rs`.
71+
- https://docs.rs/runtime-attributes - our original experiments with
72+
`async fn main`.
73+
- https://docs.rs/async-trait - async trait support by the fantastic
74+
[David Tolnay](https://github.com/dtolnay/).
75+
- https://docs.rs/futures-async-stream - for iterating and defining streams by
76+
the skilled [Taiki Endo](https://github.com/taiki-e/).
2977

3078
## License
3179
[MIT](./LICENSE-MIT) OR [Apache-2.0](./LICENSE-APACHE)

examples/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use async_std::task;
2+
3+
#[async_attributes::main]
4+
async fn main() {
5+
task::spawn(async {
6+
println!("Hello, world!");
7+
}).await;
8+
}

src/lib.rs

Lines changed: 85 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,49 @@
1-
//! Proc Macro attributes for the [Runtime](https://github.com/rustasync/runtime) crate. See the
2-
//! [Runtime](https://docs.rs/runtime) documentation for more details.
1+
//! Experimental language-level polyfills for Async Rust.
2+
//!
3+
//! # Examples
4+
//!
5+
//! ```
6+
//! use async_std::task;
7+
//!
8+
//! #[async_attributes::main]
9+
//! async fn main() {
10+
//! println!("Hello, world!");
11+
//! }
12+
//! ```
13+
//!
14+
//! # About
15+
//!
16+
//! Async Rust is a work in progress. The language has enabled us to do some
17+
//! fantastic things, but not everything is figured out yet. This crate exists
18+
//! to polyfill language-level support for async idioms before they can be part
19+
//! of the language.
20+
//!
21+
//! A great example of this is `async fn main`, which we first introduced as
22+
//! part of the [`runtime`](https://docs.rs/runtime/0.3.0-alpha.7/runtime/) crate.
23+
//! Its premise is that if `async fn` is required for every `await` call, it
24+
//! makes sense to apply that even to `fn main`. Unfortunately this would
25+
//! require compiler support to enable, so we've provided an experimental
26+
//! polyfill for it in the mean time.
27+
//!
28+
//! # Why isn't this crate part of async-std?
29+
//!
30+
//! We want to make sure `async-std`'s surface area is stable, and only includes
31+
//! things that would make sense to be part of "an async version of std".
32+
//! Language level support is really important, but _not_ part of the standard
33+
//! library.
34+
//!
35+
//! This has some distinct benefits: in particular it allows us to
36+
//! version both crates at a different pace. And as features are added to the
37+
//! language (or we decide they weren't a great idea after all), we can
38+
//! incrementally shrink the surface area of this crate.
39+
//!
40+
//! The other big benefit is that it allows libraries to depend on `async-std`
41+
//! without needing to pull in the rather heavy `syn`, `quote`, and
42+
//! `proc-macro2` crates. This should help keep compilation times snappy for
43+
//! everyone.
344
445
#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
546
#![deny(missing_debug_implementations, nonstandard_style)]
6-
#![feature(async_await)]
747
#![recursion_limit = "512"]
848

949
extern crate proc_macro;
@@ -17,28 +57,14 @@ use syn::spanned::Spanned;
1757
/// # Examples
1858
///
1959
/// ```ignore
20-
/// #![feature(async_await)]
21-
///
22-
/// #[runtime::main]
60+
/// #[async_attributes::main]
2361
/// async fn main() -> std::io::Result<()> {
2462
/// Ok(())
2563
/// }
2664
/// ```
2765
#[cfg(not(test))] // NOTE: exporting main breaks tests, we should file an issue.
2866
#[proc_macro_attribute]
29-
pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
30-
let rt = if attr.is_empty() {
31-
if cfg!(feature = "native") {
32-
syn::parse_str("runtime::native::Native").unwrap()
33-
} else {
34-
let tokens = quote_spanned! { proc_macro2::Span::call_site() =>
35-
compile_error!("async runtime needs to be specified if no default runtime is set");
36-
};
37-
return TokenStream::from(tokens);
38-
}
39-
} else {
40-
syn::parse_macro_input!(attr as syn::Expr)
41-
};
67+
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
4268
let input = syn::parse_macro_input!(item as syn::ItemFn);
4369

4470
let ret = &input.decl.output;
@@ -48,17 +74,15 @@ pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
4874
let attrs = &input.attrs;
4975

5076
if name != "main" {
51-
let tokens = quote_spanned! { name.span() =>
52-
compile_error!("only the main function can be tagged with #[runtime::main]");
53-
};
54-
return TokenStream::from(tokens);
77+
return TokenStream::from(quote_spanned! { name.span() =>
78+
compile_error!("only the main function can be tagged with #[runtime::main]"),
79+
});
5580
}
5681

5782
if input.asyncness.is_none() {
58-
let tokens = quote_spanned! { input.span() =>
59-
compile_error!("the async keyword is missing from the function declaration");
60-
};
61-
return TokenStream::from(tokens);
83+
return TokenStream::from(quote_spanned! { input.span() =>
84+
compile_error!("the async keyword is missing from the function declaration"),
85+
});
6286
}
6387

6488
let result = quote! {
@@ -68,7 +92,7 @@ pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
6892
#body
6993
}
7094

71-
runtime::raw::enter(#rt, async {
95+
async_std::task::block_on(async {
7296
main().await
7397
})
7498
}
@@ -83,27 +107,14 @@ pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
83107
/// # Examples
84108
///
85109
/// ```ignore
86-
/// #![feature(async_await)]
87-
///
88-
/// #[runtime::test]
89-
/// async fn main() -> std::io::Result<()> {
110+
/// #[async_attributes::test]
111+
/// async fn my_test() -> std::io::Result<()> {
112+
/// assert_eq(2 * 2, 4);
90113
/// Ok(())
91114
/// }
92115
/// ```
93116
#[proc_macro_attribute]
94-
pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
95-
let rt = if attr.is_empty() {
96-
if cfg!(feature = "native") {
97-
syn::parse_str("runtime::native::Native").unwrap()
98-
} else {
99-
let tokens = quote_spanned! { proc_macro2::Span::call_site() =>
100-
compile_error!("async runtime needs to be specified if no default runtime is set");
101-
};
102-
return TokenStream::from(tokens);
103-
}
104-
} else {
105-
syn::parse_macro_input!(attr as syn::Expr)
106-
};
117+
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
107118
let input = syn::parse_macro_input!(item as syn::ItemFn);
108119

109120
let ret = &input.decl.output;
@@ -112,18 +123,17 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
112123
let attrs = &input.attrs;
113124

114125
if input.asyncness.is_none() {
115-
let tokens = quote_spanned! { input.span() =>
116-
compile_error!("the async keyword is missing from the function declaration");
117-
};
118-
return TokenStream::from(tokens);
126+
return TokenStream::from(quote_spanned! { input.span() =>
127+
compile_error!("the async keyword is missing from the function declaration"),
128+
});
119129
}
120130

121131
let result = quote! {
122-
#[test]
123-
#(#attrs)*
124-
fn #name() #ret {
125-
runtime::raw::enter(#rt, async { #body })
126-
}
132+
#[test]
133+
#(#attrs)*
134+
fn #name() #ret {
135+
async_std::task::block_on(async { #body })
136+
}
127137
};
128138

129139
result.into()
@@ -134,29 +144,21 @@ pub fn test(attr: TokenStream, item: TokenStream) -> TokenStream {
134144
/// # Examples
135145
///
136146
/// ```ignore
137-
/// #![feature(async_await, test)]
147+
/// #![feature(test)]
138148
///
139149
/// extern crate test;
140150
///
141-
/// #[runtime::test]
151+
/// use async_std::task;
152+
///
153+
/// #[async_attributes::test]
142154
/// async fn spawn_and_await() {
143-
/// runtime::spawn(async {}).await;
155+
/// task::spawn(async {
156+
/// println!("hello world");
157+
/// }).await;
144158
/// }
145159
/// ```
146160
#[proc_macro_attribute]
147-
pub fn bench(attr: TokenStream, item: TokenStream) -> TokenStream {
148-
let rt = if attr.is_empty() {
149-
if cfg!(feature = "native") {
150-
syn::parse_str("runtime::native::Native").unwrap()
151-
} else {
152-
let tokens = quote_spanned! { proc_macro2::Span::call_site() =>
153-
compile_error!("async runtime needs to be specified if no default runtime is set");
154-
};
155-
return TokenStream::from(tokens);
156-
}
157-
} else {
158-
syn::parse_macro_input!(attr as syn::Expr)
159-
};
161+
pub fn bench(_attr: TokenStream, item: TokenStream) -> TokenStream {
160162
let input = syn::parse_macro_input!(item as syn::ItemFn);
161163

162164
let args = &input.decl.inputs;
@@ -165,27 +167,25 @@ pub fn bench(attr: TokenStream, item: TokenStream) -> TokenStream {
165167
let attrs = &input.attrs;
166168

167169
if input.asyncness.is_none() {
168-
let tokens = quote_spanned! { input.span() =>
169-
compile_error!("the async keyword is missing from the function declaration");
170-
};
171-
return TokenStream::from(tokens);
170+
return TokenStream::from(quote_spanned! { input.span() =>
171+
compile_error!("the async keyword is missing from the function declaration"),
172+
});
172173
}
173174

174175
if !args.is_empty() {
175-
let tokens = quote_spanned! { args.span() =>
176-
compile_error!("async benchmarks don't take any arguments");
177-
};
178-
return TokenStream::from(tokens);
176+
return TokenStream::from(quote_spanned! { args.span() =>
177+
compile_error!("async benchmarks don't take any arguments"),
178+
});
179179
}
180180

181181
let result = quote! {
182-
#[bench]
183-
#(#attrs)*
184-
fn #name(b: &mut test::Bencher) {
185-
b.iter(|| {
186-
let _ = runtime::raw::enter(#rt, async { #body });
187-
});
188-
}
182+
#[bench]
183+
#(#attrs)*
184+
fn #name(b: &mut test::Bencher) {
185+
b.iter(|| {
186+
let _ = async_std::task::block_on(async { #body });
187+
});
188+
}
189189
};
190190

191191
result.into()

0 commit comments

Comments
 (0)