min32_proc/
lib.rs

1//! Defines procedural macros for the `min32` crate.
2//!
3//! You do not need to depend on this directly; you can use `min32` where these macros are imported.
4
5#![warn(missing_docs)]
6
7use proc_macro::TokenStream;
8use proc_macro2::TokenTree;
9use quote::{quote, ToTokens};
10
11/// Defines the entry point for a Win32 executable.
12///
13/// The function can be named anything and use any Rust-callable ABI. It can also take either no
14/// parameters or these four parameters:
15/// * `hInstance: min32::HINSTANCE`
16/// * `hPrevInstance: min32::HINSTANCE`
17/// * `commandLine: *const core::ffi::c_char`
18/// * `nShowCmd: core::ffi::c_int`
19///
20/// It can optionally return an integer of `core::ffi::c_int` to indicate an exit code, but if there
21/// is no return value, then it will be treated as always returning `0`.
22#[proc_macro_attribute]
23pub fn winmain(a: TokenStream, b: TokenStream) -> TokenStream {
24    if !a.is_empty() {
25        return quote! {
26            compile_error!("winmain attribute does not take any parameters");
27        }.into()
28    };
29
30    let Ok(tokens) = syn::parse::<syn::ItemFn>(b.clone()) else {
31        return b;
32    };
33
34    let rval = tokens.sig.output.into_token_stream();
35    let function_name = &tokens.sig.ident;
36
37    let handle_rval = if !rval.is_empty() && rval.to_string() != "()" {
38        TokenTree::Ident(proc_macro2::Ident::new("_rval", proc_macro2::Span::call_site()))
39    }
40    else {
41        TokenTree::Literal(proc_macro2::Literal::i32_suffixed(0))
42    };
43
44    let inputs = tokens.sig.inputs.to_token_stream();
45    let function_call = if inputs.is_empty() {
46        quote! { super::#function_name () }
47    }
48    else {
49        quote! {
50            super::#function_name (
51                _hInstance as min32::HINSTANCE,
52                _hPrevInstance as min32::HINSTANCE,
53                _commandLine,
54                _nShowCmd
55            )
56        }
57    };
58
59    let winmain: TokenStream = quote! {
60        mod __private_winmain_impl {
61            #[unsafe(no_mangle)]
62            extern "system" fn WinMain(
63                _hInstance: min32::HINSTANCE,
64                _hPrevInstance: min32::HINSTANCE,
65                _commandLine: *const core::ffi::c_char,
66                _nShowCmd: core::ffi::c_int
67            ) -> core::ffi::c_int {
68                let _rval = unsafe { #function_call };
69                #handle_rval as core::ffi::c_int
70            }
71        }
72    }.into();
73
74    TokenStream::from_iter([winmain, b].into_iter())
75}
76
77
78/// Defines the entry point for a Win32 dynamic library.
79///
80/// The function can be named anything and use any Rust-callable ABI, but it must take these three
81/// parameters:
82/// * `hInstance: min32::HINSTANCE`
83/// * `fdwReason: u32`
84/// * `lpvReserved: *const core::ffi::c_void`
85///
86/// It can optionally return a `bool`, but if it does not return anything, then it will be treated
87/// as always returning `true`.
88#[proc_macro_attribute]
89pub fn dllmain(a: TokenStream, b: TokenStream) -> TokenStream {
90    if !a.is_empty() {
91        return quote! {
92            compile_error!("dllmain attribute does not take any parameters")
93        }.into()
94    };
95
96    let Ok(tokens) = syn::parse::<syn::ItemFn>(b.clone()) else {
97        return b;
98    };
99
100    let rval = tokens.sig.output.into_token_stream();
101    let function_name = &tokens.sig.ident;
102
103    let inputs = tokens.sig.inputs.to_token_stream();
104    if inputs.is_empty() {
105        return quote! {
106            compile_error!("DllMain functions must take three parameters");
107        }.into()
108    };
109
110    let handle_rval = if !rval.is_empty() && rval.to_string() != "()" {
111        quote! { _rval as i32 }
112    }
113    else {
114        TokenTree::Literal(proc_macro2::Literal::i32_suffixed(1)).to_token_stream()
115    };
116
117    let function_call = quote! {
118        super::#function_name (
119            _hInstance as min32::HINSTANCE,
120            _fdwReason,
121            _lpvReserved
122        )
123    };
124
125    let dllmain: TokenStream = quote! {
126        mod __private_dllmain_impl {
127            #[unsafe(no_mangle)]
128            extern "system" fn DllMain(
129                _hInstance: usize,
130                _fdwReason: u32,
131                _lpvReserved: *mut core::ffi::c_void
132            ) -> core::ffi::c_int {
133                let _rval = unsafe { #function_call };
134                #handle_rval
135            }
136        }
137    }.into();
138
139    TokenStream::from_iter([dllmain, b].into_iter())
140}