Skip to content

Commit 67177aa

Browse files
committed
feat(macros): builder params
1 parent 1edb78c commit 67177aa

7 files changed

Lines changed: 229 additions & 106 deletions

File tree

compio-macros/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ async fn main() {
3939
println!("Hello from compio!");
4040
}
4141
```
42+
43+
You can customize the runtime through params:
44+
45+
```rust
46+
#[compio::main(event_interval = 4, with_proactor(capacity = 16))]
47+
async fn main() {
48+
println!("Hello from compio!");
49+
}
50+
```

compio-macros/src/item_fn.rs

Lines changed: 168 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,114 @@
11
use proc_macro2::TokenStream;
2-
use quote::quote;
2+
use quote::{ToTokens, TokenStreamExt, quote};
33
use syn::{
4-
Attribute, Expr, Lit, Meta, Signature, Visibility, parse::Parse, punctuated::Punctuated,
4+
Attribute, Expr, ExprLit, Ident, Lit, Meta, Path, Signature, Token, Visibility, parse::Parse,
5+
punctuated::Punctuated,
56
};
67

7-
type AttributeArgs = Punctuated<syn::Meta, syn::Token![,]>;
8+
use crate::{retrieve_driver_mod, retrieve_runtime_mod};
89

10+
struct MetaPunctuated(Punctuated<Meta, Token![,]>);
11+
12+
impl Parse for MetaPunctuated {
13+
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
14+
Ok(Self(Punctuated::parse_terminated(input)?))
15+
}
16+
}
17+
18+
pub(crate) struct BuilderMethod {
19+
pub name: Ident,
20+
pub value: Expr,
21+
}
22+
23+
#[derive(Default)]
924
pub(crate) struct RawAttr {
10-
pub inner_attrs: AttributeArgs,
25+
pub runtime_methods: Vec<BuilderMethod>,
26+
pub proactor_methods: Vec<BuilderMethod>,
27+
pub crate_name: Option<TokenStream>,
28+
pub with_proactor_call: Option<Path>,
1129
}
1230

1331
impl Parse for RawAttr {
1432
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
15-
let inner_attrs = AttributeArgs::parse_terminated(input)?;
16-
Ok(Self { inner_attrs })
33+
let items = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
34+
35+
let mut runtime_methods = Vec::new();
36+
let mut proactor_methods = Vec::new();
37+
let mut crate_name = None;
38+
let mut with_proactor_call = None;
39+
40+
for meta in items {
41+
match meta {
42+
Meta::List(list) => {
43+
if list.path.is_ident("with_proactor") {
44+
if !list.tokens.is_empty() {
45+
let inner_items = syn::parse2::<MetaPunctuated>(list.tokens)?.0;
46+
for inner_meta in inner_items {
47+
if let Meta::NameValue(nv) = inner_meta {
48+
let name = nv.path.require_ident()?.clone();
49+
proactor_methods.push(BuilderMethod {
50+
name,
51+
value: nv.value,
52+
});
53+
} else {
54+
return Err(syn::Error::new_spanned(
55+
inner_meta,
56+
"expected `name = value` inside `with_proactor`",
57+
));
58+
}
59+
}
60+
with_proactor_call = Some(list.path.clone());
61+
}
62+
} else {
63+
return Err(syn::Error::new_spanned(
64+
list.path,
65+
"unknown key; use `name = value` for parameters or \
66+
`with_proactor(...)` for proactor config",
67+
));
68+
}
69+
}
70+
Meta::NameValue(nv) => {
71+
if nv.path.is_ident("crate") {
72+
if let Expr::Lit(ExprLit {
73+
lit: Lit::Str(s), ..
74+
}) = &nv.value
75+
{
76+
crate_name = Some(s.parse::<TokenStream>().unwrap());
77+
} else {
78+
return Err(syn::Error::new_spanned(
79+
&nv.value,
80+
"expected a string literal for `crate`",
81+
));
82+
}
83+
} else {
84+
let name = nv.path.require_ident()?.clone();
85+
runtime_methods.push(BuilderMethod {
86+
name,
87+
value: nv.value,
88+
});
89+
}
90+
}
91+
Meta::Path(path) => {
92+
return Err(syn::Error::new_spanned(
93+
path,
94+
"expected `name = value` or `with_proactor(...)`",
95+
));
96+
}
97+
}
98+
}
99+
100+
Ok(Self {
101+
runtime_methods,
102+
proactor_methods,
103+
crate_name,
104+
with_proactor_call,
105+
})
17106
}
18107
}
19108

20109
pub(crate) struct RawBodyItemFn {
21110
pub attrs: Vec<Attribute>,
22-
pub args: AttributeArgs,
111+
pub args: RawAttr,
23112
pub vis: Visibility,
24113
pub sig: Signature,
25114
pub body: TokenStream,
@@ -29,37 +118,88 @@ impl RawBodyItemFn {
29118
pub fn new(attrs: Vec<Attribute>, vis: Visibility, sig: Signature, body: TokenStream) -> Self {
30119
Self {
31120
attrs,
32-
args: AttributeArgs::new(),
121+
args: RawAttr::default(),
33122
vis,
34123
sig,
35124
body,
36125
}
37126
}
38127

39-
pub fn set_args(&mut self, args: AttributeArgs) {
128+
pub fn set_args(&mut self, args: RawAttr) {
40129
self.args = args;
41130
}
42131

43-
pub fn crate_name(&self) -> Option<TokenStream> {
44-
for attr in &self.args {
45-
if let Meta::NameValue(name) = &attr {
46-
let ident = name
47-
.path
48-
.get_ident()
49-
.map(|ident| ident.to_string().to_lowercase())
50-
.unwrap_or_default();
51-
if ident == "crate" {
52-
if let Expr::Lit(lit) = &name.value
53-
&& let Lit::Str(s) = &lit.lit
54-
{
55-
let crate_name = s.parse::<TokenStream>().unwrap();
56-
return Some(quote!(#crate_name::runtime));
57-
}
58-
} else {
59-
panic!("Unsupported property {ident}");
60-
}
132+
pub fn emit_fn_to_tokens(&self, tokens: &mut TokenStream) {
133+
tokens.append_all(
134+
self.attrs
135+
.iter()
136+
.filter(|a| matches!(a.style, syn::AttrStyle::Outer)),
137+
);
138+
self.vis.to_tokens(tokens);
139+
self.sig.to_tokens(tokens);
140+
tokens.append_all(self.gen_runtime_block());
141+
}
142+
143+
fn gen_runtime_block(&self) -> TokenStream {
144+
let runtime_mod = match &self.args.crate_name {
145+
Some(c) => {
146+
let c = c.clone();
147+
quote!(#c::runtime)
148+
}
149+
None => retrieve_runtime_mod(),
150+
};
151+
152+
let driver_mod = match &self.args.crate_name {
153+
Some(c) => {
154+
let c = c.clone();
155+
quote!(#c::driver)
61156
}
157+
None => retrieve_driver_mod(),
158+
};
159+
160+
let block = &self.body;
161+
162+
let mut builder = quote! {
163+
#runtime_mod::Runtime::builder()
164+
};
165+
166+
for method in &self.args.runtime_methods {
167+
let name = &method.name;
168+
let value = &method.value;
169+
builder = quote! {
170+
#builder.#name(#value)
171+
};
62172
}
63-
None
173+
174+
if !self.args.proactor_methods.is_empty() {
175+
let mut proactor_stmts: Vec<TokenStream> = Vec::new();
176+
proactor_stmts.push(quote! {
177+
let mut __compio_proactor_builder = #driver_mod::Proactor::builder();
178+
});
179+
for method in &self.args.proactor_methods {
180+
let name = &method.name;
181+
let value = &method.value;
182+
proactor_stmts.push(quote! {
183+
__compio_proactor_builder.#name(#value);
184+
});
185+
}
186+
// Preserve the original token for the `with_proactor` call to make the language
187+
// server work better.
188+
let with_proactor_call = if let Some(path) = &self.args.with_proactor_call {
189+
quote!(#path)
190+
} else {
191+
quote!(with_proactor)
192+
};
193+
builder = quote! {
194+
#builder.#with_proactor_call({
195+
#(#proactor_stmts)*
196+
__compio_proactor_builder
197+
})
198+
};
199+
}
200+
201+
quote!({
202+
#builder.build().expect("cannot create runtime").block_on(async move #block)
203+
})
64204
}
65205
}

compio-macros/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,24 @@ fn retrieve_runtime_mod() -> proc_macro2::TokenStream {
5050
},
5151
}
5252
}
53+
54+
fn retrieve_driver_mod() -> proc_macro2::TokenStream {
55+
match crate_name("compio-driver") {
56+
Ok(FoundCrate::Itself) => quote!(crate),
57+
Ok(FoundCrate::Name(name)) => {
58+
let ident = Ident::new(&name, Span::call_site());
59+
quote!(::#ident)
60+
}
61+
Err(_) => match crate_name("compio") {
62+
Ok(FoundCrate::Itself) => quote!(crate::driver),
63+
Ok(FoundCrate::Name(name)) => {
64+
let ident = Ident::new(&name, Span::call_site());
65+
quote!(::#ident::driver)
66+
}
67+
Err(_) => {
68+
let ident = Ident::new("compio_driver", Span::call_site());
69+
quote!(::#ident)
70+
}
71+
},
72+
}
73+
}

compio-macros/src/main_fn.rs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
use proc_macro2::TokenStream;
2-
use quote::{ToTokens, TokenStreamExt, quote};
3-
use syn::{AttrStyle, Attribute, Signature, Visibility, parse::Parse};
2+
use quote::ToTokens;
3+
use syn::{Attribute, Signature, Visibility, parse::Parse};
44

5-
use crate::{
6-
item_fn::{RawAttr, RawBodyItemFn},
7-
retrieve_runtime_mod,
8-
};
5+
use crate::item_fn::{RawAttr, RawBodyItemFn};
96

107
pub(crate) struct CompioMain(pub RawBodyItemFn);
118

129
impl CompioMain {
1310
pub fn with_args(mut self, args: RawAttr) -> Self {
14-
self.0.set_args(args.inner_attrs);
11+
self.0.set_args(args);
1512
self
1613
}
1714
}
@@ -37,18 +34,6 @@ impl Parse for CompioMain {
3734

3835
impl ToTokens for CompioMain {
3936
fn to_tokens(&self, tokens: &mut TokenStream) {
40-
tokens.append_all(
41-
self.0
42-
.attrs
43-
.iter()
44-
.filter(|a| matches!(a.style, AttrStyle::Outer)),
45-
);
46-
self.0.vis.to_tokens(tokens);
47-
self.0.sig.to_tokens(tokens);
48-
let block = &self.0.body;
49-
let runtime_mod = self.0.crate_name().unwrap_or_else(retrieve_runtime_mod);
50-
tokens.append_all(quote!({
51-
#runtime_mod::Runtime::new().expect("cannot create runtime").block_on(async move #block)
52-
}));
37+
self.0.emit_fn_to_tokens(tokens);
5338
}
5439
}

compio-macros/src/test_fn.rs

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
use proc_macro2::TokenStream;
22
use quote::{ToTokens, TokenStreamExt, quote};
3-
use syn::{AttrStyle, Attribute, Signature, Visibility, parse::Parse};
3+
use syn::{Attribute, Signature, Visibility, parse::Parse};
44

5-
use crate::{
6-
item_fn::{RawAttr, RawBodyItemFn},
7-
retrieve_runtime_mod,
8-
};
5+
use crate::item_fn::{RawAttr, RawBodyItemFn};
96

107
pub(crate) struct CompioTest(pub RawBodyItemFn);
118

129
impl CompioTest {
1310
pub fn with_args(mut self, args: RawAttr) -> Self {
14-
self.0.set_args(args.inner_attrs);
11+
self.0.set_args(args);
1512
self
1613
}
1714
}
@@ -37,18 +34,6 @@ impl Parse for CompioTest {
3734
impl ToTokens for CompioTest {
3835
fn to_tokens(&self, tokens: &mut TokenStream) {
3936
tokens.append_all(quote!(#[test]));
40-
tokens.append_all(
41-
self.0
42-
.attrs
43-
.iter()
44-
.filter(|a| matches!(a.style, AttrStyle::Outer)),
45-
);
46-
self.0.vis.to_tokens(tokens);
47-
self.0.sig.to_tokens(tokens);
48-
let block = &self.0.body;
49-
let runtime_mod = self.0.crate_name().unwrap_or_else(retrieve_runtime_mod);
50-
tokens.append_all(quote!({
51-
#runtime_mod::Runtime::new().expect("cannot create runtime").block_on(async move #block)
52-
}));
37+
self.0.emit_fn_to_tokens(tokens);
5338
}
5439
}

compio/tests/accept.rs

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,18 @@
11
use compio::{
2-
driver::{DriverType, ProactorBuilder},
2+
driver::DriverType,
33
net::{TcpListener, TcpStream},
4-
runtime::Runtime,
54
};
65
use compio_runtime::ResumeUnwind;
76

8-
#[test]
9-
fn accept() {
10-
let mut proactor_builder = ProactorBuilder::new();
11-
proactor_builder.driver_type(DriverType::Poll);
12-
let runtime = Runtime::builder()
13-
.with_proactor(proactor_builder)
14-
.build()
15-
.unwrap();
16-
runtime.block_on(async {
17-
let listener = TcpListener::bind("localhost:0").await.unwrap();
18-
let addr = listener.local_addr().unwrap();
19-
let task = compio_runtime::spawn(async move {
20-
let (socket, _) = listener.accept().await.unwrap();
21-
socket
22-
});
23-
let cli = TcpStream::connect(&addr).await.unwrap();
24-
let srv = task.await.resume_unwind().expect("shouldn't be cancelled");
25-
assert_eq!(cli.local_addr().unwrap(), srv.peer_addr().unwrap());
26-
})
7+
#[compio_macros::test(with_proactor(driver_type = DriverType::Poll))]
8+
async fn accept() {
9+
let listener = TcpListener::bind("localhost:0").await.unwrap();
10+
let addr = listener.local_addr().unwrap();
11+
let task = compio_runtime::spawn(async move {
12+
let (socket, _) = listener.accept().await.unwrap();
13+
socket
14+
});
15+
let cli = TcpStream::connect(&addr).await.unwrap();
16+
let srv = task.await.resume_unwind().expect("shouldn't be cancelled");
17+
assert_eq!(cli.local_addr().unwrap(), srv.peer_addr().unwrap());
2718
}

0 commit comments

Comments
 (0)