Skip to content

Commit f474d58

Browse files
committed
glib-macros: main macro for simple async applications
1 parent 3ef9422 commit f474d58

File tree

4 files changed

+84
-31
lines changed

4 files changed

+84
-31
lines changed

examples/gio_async_tls/main.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,8 @@ use std::pin::Pin;
77

88
use std::error::Error;
99

10-
fn main() -> Result<(), Box<dyn Error>> {
11-
// Get the default main context and run our async function on it
12-
let main_context = glib::MainContext::default();
13-
main_context.block_on(run())
14-
}
15-
16-
async fn run() -> Result<(), Box<dyn Error>> {
10+
#[glib::main]
11+
async fn main() -> Result<(), Box<dyn Error>> {
1712
// Connect to https://www.rust-lang.org
1813
let client = gio::SocketClient::new();
1914
let connectable = gio::NetworkAddress::new("www.rust-lang.org", 443);
Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,43 @@
1+
use futures_channel::oneshot;
12
use std::future::pending;
2-
use std::thread;
33
use std::time::Duration;
44

55
use gio::prelude::*;
66

77
use futures::prelude::*;
88

9+
const TIMEOUT: Duration = Duration::from_secs(3);
10+
911
/// A very long task. This task actually never ends.
1012
async fn a_very_long_task() {
1113
println!("Very long task started");
1214
pending().await
1315
}
1416

15-
fn main() {
16-
const TIMEOUT: Duration = Duration::from_secs(3);
17+
#[glib::main]
18+
async fn main() {
19+
let (tx, rx) = oneshot::channel();
1720

18-
let main_ctx = glib::MainContext::default();
19-
let main_loop = glib::MainLoop::new(Some(&main_ctx), false);
2021
let cancellable = gio::Cancellable::new();
2122

22-
{
23-
let main_loop = main_loop.clone();
24-
25-
// We wrap `a_very_long_task` inside a `CancellableFuture` controlled by `cancellable`.
26-
// The task is cancelled when `.cancel()` is invoked.
27-
let cancellable_task = gio::CancellableFuture::new(a_very_long_task(), cancellable.clone())
28-
.map(move |res| {
29-
if let Err(error) = res {
30-
println!("{:?}", error);
31-
}
23+
// We wrap `a_very_long_task` inside a `CancellableFuture` controlled by `cancellable`.
24+
// The task is cancelled when `.cancel()` is invoked.
25+
let cancellable_task = gio::CancellableFuture::new(a_very_long_task(), cancellable.clone())
26+
.map(move |res| {
27+
if let Err(cancelled) = res {
28+
println!("{:?}", cancelled);
29+
}
3230

33-
main_loop.quit();
34-
});
31+
tx.send(()).unwrap();
32+
});
3533

36-
main_ctx.spawn_local(cancellable_task);
37-
}
34+
// Spawn the cancellable task.
35+
glib::MainContext::default().spawn(cancellable_task);
3836

3937
// We simulate a timeout here.
4038
// After `TIMEOUT` we cancel the pending task.
41-
thread::spawn(move || {
42-
thread::sleep(TIMEOUT);
39+
glib::MainContext::default().spawn(async move {
40+
glib::timeout_future(TIMEOUT).await;
4341

4442
println!(
4543
"Timeout ({:?}) elapsed! Cancelling pending task...",
@@ -49,5 +47,5 @@ fn main() {
4947
cancellable.cancel();
5048
});
5149

52-
main_loop.run();
50+
rx.await.unwrap();
5351
}

glib-macros/src/lib.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod utils;
1616

1717
use proc_macro::TokenStream;
1818
use proc_macro_error::proc_macro_error;
19+
use quote::ToTokens;
1920
use syn::{parse_macro_input, DeriveInput, NestedMeta};
2021

2122
/// Macro for passing variables as strong or weak references into a closure.
@@ -820,3 +821,62 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
820821
)
821822
.unwrap_or_else(|e| e.into_compile_error().into())
822823
}
824+
825+
/// Marks an async main with a default main context.
826+
///
827+
/// # Examples
828+
///
829+
/// ```
830+
/// #[glib::main]
831+
/// async fn main() {
832+
/// glib::timeout_future_seconds(1).await;
833+
/// }
834+
/// ```
835+
#[proc_macro_attribute]
836+
pub fn main(_args: TokenStream, mut item: TokenStream) -> TokenStream {
837+
let mut item_fn: syn::ItemFn = match syn::parse(item.clone()) {
838+
Ok(it) => it,
839+
Err(e) => {
840+
item.extend(TokenStream::from(e.into_compile_error()));
841+
return item;
842+
}
843+
};
844+
845+
if item_fn.sig.ident != "main" {
846+
item.extend(TokenStream::from(
847+
syn::Error::new_spanned(
848+
item_fn.sig.ident,
849+
"The main-macro can only be applied to 'main' function",
850+
)
851+
.into_compile_error(),
852+
));
853+
854+
return item;
855+
}
856+
857+
if item_fn.sig.asyncness.is_none() {
858+
item.extend(TokenStream::from(
859+
syn::Error::new_spanned(
860+
item_fn.sig.ident,
861+
"The 'async' keyword is missing from the function declaration",
862+
)
863+
.into_compile_error(),
864+
));
865+
866+
return item;
867+
}
868+
869+
item_fn.sig.asyncness = None;
870+
871+
let body = &item_fn.block;
872+
873+
item_fn.block = syn::parse2(quote::quote! {
874+
{
875+
let main_ctx = glib::MainContext::default();
876+
main_ctx.block_on(async #body)
877+
}
878+
})
879+
.expect("Body parsing failure");
880+
881+
TokenStream::from(item_fn.into_token_stream())
882+
}

glib/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ pub use bitflags;
1717
pub use once_cell;
1818

1919
pub use glib_macros::{
20-
clone, closure, closure_local, flags, object_interface, object_subclass, Boxed, Downgrade,
21-
Enum, ErrorDomain, SharedBoxed, Variant,
20+
clone, closure, closure_local, flags, main, object_interface, object_subclass, Boxed,
21+
Downgrade, Enum, ErrorDomain, SharedBoxed, Variant,
2222
};
2323

2424
#[doc(hidden)]

0 commit comments

Comments
 (0)