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);
			}
		})
	}
}