show_image_macros/
lib.rs

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