1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
//! This crate contains helper macros for the `show-image` crate.
//! You should not depend on this crate directly.
//! Instead, enable the `macro` feature of the `show-image` crate and use them from there.
/// Wrap your program entry point for correct initialization of the `show-image` global context.
///
/// The `show-image` global context will run in the main thread,
/// and your own entry point will be executed in a new thread.
/// When your entry point returns, the whole process will exit.
///
/// Any background tasks spawned by `show-image` will be joined before the process terminates,
/// but any threads spawned by user code will be killed.
/// You should manually join those if needed.
///
/// Note that we are very sorry about stealing your main thread.
/// We would rather let you keep the main thread and run the global context in a background thread.
/// However, some platforms require all GUI code to run in the "main" thread (looking at you, OS X).
/// To ensure portability, the same restriction is enforced on other platforms.
///
/// # Examples
///
/// ```no_run
/// use show_image::{ContextProxy, WindowOptions};
/// # use std::error::Error;
/// # mod image {
/// # pub fn open(_path: impl AsRef<std::path::Path>) -> Result<show_image::ImageView<'static>, std::io::Error> {
/// # Ok(show_image::ImageView::new(show_image::ImageInfo::rgb8(1, 1), &[255, 0, 0]))
/// # }
/// # }
///
/// #[show_image::main]
/// fn main() -> Result<(), Box<dyn Error>> {
/// let window = show_image::create_window("My Awesome Window", WindowOptions::default())?;
/// let image = image::open("/path/to/image.png")?;
///
/// window.set_image("image", image)?;
/// window.wait_until_destroyed()?;
/// Ok(())
/// }
/// ```
#[proc_macro_attribute]
pub fn main(attribs: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match details::main(attribs.into(), input.into()) {
Ok(x) => x.into(),
Err(e) => details::error_to_tokens(e).into(),
}
}
mod details {
use quote::quote;
/// Convert a syn::Error into a compile error with a dummy main.
///
/// The dummy main prevents the compiler from complaining about a missing entry point, which is confusing noise.
/// We only want the compiler to show the real error.
pub fn error_to_tokens(error: syn::Error) -> proc_macro2::TokenStream {
let error = error.to_compile_error();
quote! {
#error
fn main() {
panic!("#[show_image::main]: compilation should have failed, please report this bug");
}
}
}
pub fn main(arguments: proc_macro2::TokenStream, input: proc_macro2::TokenStream) -> syn::Result<proc_macro2::TokenStream> {
if !arguments.is_empty() {
return Err(syn::Error::new_spanned(arguments, "unexpected macro arguments"));
}
let function: syn::ItemFn = syn::parse2(input)?;
let name = function.sig.ident.clone();
Ok(quote! {
fn main() {
#function
::show_image::run_context(#name);
}
})
}
}