diff --git a/examples/gio_async_tls/main.rs b/examples/gio_async_tls/main.rs index bc73ee005870..826c7fd03137 100644 --- a/examples/gio_async_tls/main.rs +++ b/examples/gio_async_tls/main.rs @@ -7,13 +7,8 @@ use std::pin::Pin; use std::error::Error; -fn main() -> Result<(), Box> { - // Get the default main context and run our async function on it - let main_context = glib::MainContext::default(); - main_context.block_on(run()) -} - -async fn run() -> Result<(), Box> { +#[glib::main] +async fn main() -> Result<(), Box> { // Connect to https://www.rust-lang.org let client = gio::SocketClient::new(); let connectable = gio::NetworkAddress::new("www.rust-lang.org", 443); diff --git a/examples/gio_cancellable_future/main.rs b/examples/gio_cancellable_future/main.rs index 0aabca134393..c73683b1da05 100644 --- a/examples/gio_cancellable_future/main.rs +++ b/examples/gio_cancellable_future/main.rs @@ -1,45 +1,43 @@ +use futures_channel::oneshot; use std::future::pending; -use std::thread; use std::time::Duration; use gio::prelude::*; use futures::prelude::*; +const TIMEOUT: Duration = Duration::from_secs(3); + /// A very long task. This task actually never ends. async fn a_very_long_task() { println!("Very long task started"); pending().await } -fn main() { - const TIMEOUT: Duration = Duration::from_secs(3); +#[glib::main] +async fn main() { + let (tx, rx) = oneshot::channel(); - let main_ctx = glib::MainContext::default(); - let main_loop = glib::MainLoop::new(Some(&main_ctx), false); let cancellable = gio::Cancellable::new(); - { - let main_loop = main_loop.clone(); - - // We wrap `a_very_long_task` inside a `CancellableFuture` controlled by `cancellable`. - // The task is cancelled when `.cancel()` is invoked. - let cancellable_task = gio::CancellableFuture::new(a_very_long_task(), cancellable.clone()) - .map(move |res| { - if let Err(error) = res { - println!("{:?}", error); - } + // We wrap `a_very_long_task` inside a `CancellableFuture` controlled by `cancellable`. + // The task is cancelled when `.cancel()` is invoked. + let cancellable_task = gio::CancellableFuture::new(a_very_long_task(), cancellable.clone()) + .map(move |res| { + if let Err(cancelled) = res { + println!("{:?}", cancelled); + } - main_loop.quit(); - }); + tx.send(()).unwrap(); + }); - main_ctx.spawn_local(cancellable_task); - } + // Spawn the cancellable task. + glib::MainContext::default().spawn(cancellable_task); // We simulate a timeout here. // After `TIMEOUT` we cancel the pending task. - thread::spawn(move || { - thread::sleep(TIMEOUT); + glib::MainContext::default().spawn(async move { + glib::timeout_future(TIMEOUT).await; println!( "Timeout ({:?}) elapsed! Cancelling pending task...", @@ -49,5 +47,5 @@ fn main() { cancellable.cancel(); }); - main_loop.run(); + rx.await.unwrap(); } diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index 855090c333b7..1c05d3f2265c 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -16,6 +16,7 @@ mod utils; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; +use quote::ToTokens; use syn::{parse_macro_input, DeriveInput, NestedMeta}; /// Macro for passing variables as strong or weak references into a closure. @@ -820,3 +821,62 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream { ) .unwrap_or_else(|e| e.into_compile_error().into()) } + +/// Marks an async main with a default main context. +/// +/// # Examples +/// +/// ``` +/// #[glib::main] +/// async fn main() { +/// glib::timeout_future_seconds(1).await; +/// } +/// ``` +#[proc_macro_attribute] +pub fn main(_args: TokenStream, mut item: TokenStream) -> TokenStream { + let mut item_fn: syn::ItemFn = match syn::parse(item.clone()) { + Ok(it) => it, + Err(e) => { + item.extend(TokenStream::from(e.into_compile_error())); + return item; + } + }; + + if item_fn.sig.ident != "main" { + item.extend(TokenStream::from( + syn::Error::new_spanned( + item_fn.sig.ident, + "The main-macro can only be applied to 'main' function", + ) + .into_compile_error(), + )); + + return item; + } + + if item_fn.sig.asyncness.is_none() { + item.extend(TokenStream::from( + syn::Error::new_spanned( + item_fn.sig.ident, + "The 'async' keyword is missing from the function declaration", + ) + .into_compile_error(), + )); + + return item; + } + + item_fn.sig.asyncness = None; + + let body = &item_fn.block; + + item_fn.block = syn::parse2(quote::quote! { + { + let main_ctx = glib::MainContext::default(); + main_ctx.block_on(async #body) + } + }) + .expect("Body parsing failure"); + + TokenStream::from(item_fn.into_token_stream()) +} diff --git a/glib/src/lib.rs b/glib/src/lib.rs index 8f94324337ba..447a6faadcb7 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -17,8 +17,8 @@ pub use bitflags; pub use once_cell; pub use glib_macros::{ - clone, closure, closure_local, flags, object_interface, object_subclass, Boxed, Downgrade, - Enum, ErrorDomain, SharedBoxed, Variant, + clone, closure, closure_local, flags, main, object_interface, object_subclass, Boxed, + Downgrade, Enum, ErrorDomain, SharedBoxed, Variant, }; #[doc(hidden)]