orengine_macros/lib.rs
1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::parse_macro_input;
6
7/// Generates code for [`Future::poll`](std::future::Future::poll).
8///
9/// # Must have above
10///
11/// * `this` with `io_request_data` (`Option<IoRequestData>`) fields;
12///
13/// * `cx` with `waker` method that returns ([`Waker`](std::task::Waker)) which contains
14/// `*const orengine::runtime::Task` in [`data`](std::task::Waker::data);
15///
16/// * declared variable `ret` (`usize`) which can be used in `ret_statement`.
17///
18/// # Arguments
19///
20/// * `do_request` - the request code
21/// * `ret_statement` - the return statement which can use `ret`(`usize`) variable.
22#[proc_macro]
23pub fn poll_for_io_request(input: TokenStream) -> TokenStream {
24 let input_elems = parse_macro_input!(input as syn::ExprTuple).elems;
25
26 let do_request = &input_elems[0];
27 let ret_statement = &input_elems[1];
28
29 let expanded = quote! {
30 if let Some(mut io_request_data) = this.io_request_data.take() {
31 match io_request_data.ret() {
32 Ok(io_request_data_ret) => {
33 ret = io_request_data_ret;
34 return Poll::Ready(Ok(#ret_statement));
35 }
36 Err(err) => {
37 unsafe {
38 return Poll::Ready(Err(err));
39 }
40 }
41 }
42 }
43
44 let task = unsafe { orengine::get_task_from_context!(cx) };
45 this.io_request_data = Some(IoRequestData::new(task));
46
47 #do_request;
48
49 return Poll::Pending;
50 };
51
52 TokenStream::from(expanded)
53}
54
55/// Generates code for [`Future::poll`](std::future::Future::poll).
56///
57/// # The difference between `poll_for_io_request` and `poll_for_time_bounded_io_request`
58///
59/// `poll_for_time_bounded_io_request` deregisters the time bounded task after execution.
60///
61/// # Must have above
62///
63/// * `this` with `io_request_data` (`Option<IoRequestData>`) and `deadline`
64/// ([`Instant`](std::time::Instant)) fields;
65///
66/// * `cx` with `waker` method that returns ([`Waker`](std::task::Waker)) which contains
67/// `*const orengine::runtime::Task` in [`data`](std::task::Waker::data);
68///
69/// * declared variable `ret` (`usize`) which can be used in `ret_statement`;
70///
71/// * `worker` from `local_worker()`.
72///
73/// # Arguments
74///
75/// * `do_request` - the request code
76/// * `ret_statement` - the return statement which can use `ret`(`usize`) variable.
77#[proc_macro]
78pub fn poll_for_time_bounded_io_request(input: TokenStream) -> TokenStream {
79 let input_elems = parse_macro_input!(input as syn::ExprTuple).elems;
80
81 let do_request = &input_elems[0];
82 let ret_statement = &input_elems[1];
83
84 let expanded = quote! {
85 if let Some(mut io_request_data) = this.io_request_data.take() {
86 match io_request_data.ret() {
87 Ok(io_request_data_ret) => {
88 ret = io_request_data_ret;
89 worker.deregister_time_bounded_io_task(&this.deadline);
90
91 return Poll::Ready(Ok(#ret_statement));
92 }
93 Err(err) => {
94 if err.kind() != std::io::ErrorKind::TimedOut {
95 worker.deregister_time_bounded_io_task(&this.deadline);
96 }
97
98 return Poll::Ready(Err(err));
99 }
100 }
101 }
102
103 let task = unsafe { orengine::get_task_from_context!(cx) };
104 this.io_request_data = Some(IoRequestData::new(task));
105
106 #do_request;
107
108 return Poll::Pending;
109 };
110
111 TokenStream::from(expanded)
112}
113
114/// Generates a test function with provided locality.
115fn generate_test(input: TokenStream, is_local: bool) -> TokenStream {
116 let input = parse_macro_input!(input as syn::ItemFn);
117 let body = &input.block;
118 let attrs = &input.attrs;
119 let signature = &input.sig;
120 let name = &signature.ident;
121 let name_str = name.to_string();
122 if signature.inputs.len() > 0 {
123 panic!("Test function must have zero arguments!");
124 }
125 let spawn_fn = if is_local {
126 quote! { orengine::test::run_test_and_block_on_local }
127 } else {
128 quote! { orengine::test::run_test_and_block_on_shared }
129 };
130
131 let expanded = quote! {
132 #[test]
133 #(#attrs)*
134 fn #name() {
135 println!("Test {} started!", #name_str.to_string());
136 #spawn_fn(async {
137 #body
138 });
139 println!("Test {} finished!", #name_str.to_string());
140 }
141 };
142
143 TokenStream::from(expanded)
144}
145
146/// Generates a test function with running an `Executor` with `local` task.
147///
148/// # The difference between `test_local` and [`test_shared`]
149///
150/// `test_local` generates a test function that runs an `Executor` with `local` task.
151/// [`test_shared`] generates a test function that runs an `Executor` with `shared` task.
152///
153/// # Example
154///
155/// ```ignore
156/// #[orengine_macros::test_local]
157/// fn test_sleep() {
158/// let start = std::time::Instant::now();
159/// orengine::sleep(std::time::Duration::from_secs(1)).await;
160/// assert!(start.elapsed() >= std::time::Duration::from_secs(1));
161/// }
162/// ```
163///
164/// # Note
165///
166/// Code above is equal to:
167///
168/// ```ignore
169/// #[test]
170/// fn test_sleep() {
171/// println!("Test sleep started!");
172/// orengine::test::run_test_and_block_on_local(async {
173/// let start = std::time::Instant::now();
174/// orengine::sleep(std::time::Duration::from_secs(1)).await;
175/// assert!(start.elapsed() >= std::time::Duration::from_secs(1));
176/// });
177/// println!("Test sleep finished!");
178/// }
179/// ```
180#[proc_macro_attribute]
181pub fn test_local(_: TokenStream, input: TokenStream) -> TokenStream {
182 generate_test(input, true)
183}
184
185/// Generates a test function with running an `Executor` with `local` task.
186///
187/// # The difference between `test_shared` and [`test_local`]
188///
189/// [`test_shared`] generates a test function that runs an `Executor` with `shared` task.
190/// `test_local` generates a test function that runs an `Executor` with `local` task.
191///
192/// # Example
193///
194/// ```ignore
195/// #[orengine_macros::test_shared]
196/// fn test_sleep() {
197/// let start = std::time::Instant::now();
198/// orengine::sleep(std::time::Duration::from_secs(1)).await;
199/// assert!(start.elapsed() >= std::time::Duration::from_secs(1));
200/// }
201/// ```
202///
203/// # Note
204///
205/// Code above is equal to:
206///
207/// ```ignore
208/// #[test]
209/// fn test_sleep() {
210/// println!("Test sleep started!");
211/// orengine::test::run_test_and_block_on_shared(async {
212/// let start = std::time::Instant::now();
213/// orengine::sleep(std::time::Duration::from_secs(1)).await;
214/// assert!(start.elapsed() >= std::time::Duration::from_secs(1));
215/// });
216/// println!("Test sleep finished!");
217/// }
218/// ```
219#[proc_macro_attribute]
220pub fn test_shared(_: TokenStream, input: TokenStream) -> TokenStream {
221 generate_test(input, false)
222}