gl_headless_macros/
lib.rs1#![forbid(unsafe_code)]
6#![forbid(elided_lifetimes_in_paths)]
7
8use std::fmt;
9
10use proc_macro::TokenStream;
11use quote::{quote_spanned, ToTokens};
12use syn::spanned::Spanned;
13use syn::{parse_macro_input, FnArg, ItemFn, LitStr, Pat};
14
15#[proc_macro_attribute]
47pub fn gl_headless(args: TokenStream, item: TokenStream) -> TokenStream {
48 let mut version: Option<LitStr> = None;
49 let args_parser = syn::meta::parser(|meta| {
50 if meta.path.is_ident("version") {
51 version = Some(meta.value()?.parse()?);
52 Ok(())
53 } else {
54 Err(meta.error("unsupported attribute"))
55 }
56 });
57 parse_macro_input!(args with args_parser);
58
59 let item_fn: ItemFn = parse_macro_input!(item);
60 let attrs = &item_fn.attrs;
61 let vis = &item_fn.vis;
62 let sig = &item_fn.sig;
63 let ident = &sig.ident;
64
65 let mut new_sig = sig.clone();
66
67 let mut args = Vec::with_capacity(sig.inputs.len());
68 for arg in &sig.inputs {
69 let arg = match arg {
70 FnArg::Typed(arg) => arg,
71 FnArg::Receiver(_arg) => {
72 return error(arg, "associated methods not supported currently")
73 }
74 };
75 let ident = match arg.pat.as_ref() {
76 Pat::Ident(ident) => ident,
77 _ => {
78 return error(arg, "pattern not supported currently");
79 }
80 };
81 args.push(&ident.ident);
82 }
83
84 let call_wrap_unsafe = sig.unsafety.is_some() && (ident.to_string() == "main");
85 let call = if call_wrap_unsafe {
86 new_sig.unsafety = None;
87
88 quote_spanned! { sig.span() =>
89 unsafe {
90 #ident(
91 #(#args),*
92 )
93 }
94 }
95 } else {
96 quote_spanned! { sig.span() =>
97 #ident(
98 #(#args),*
99 )
100 }
101 };
102
103 let set_version_str = version.map(|version| {
104 quote_spanned! { version.span() =>
105 builder.set_version_str(#version);
106 }
107 });
108
109 quote_spanned! { sig.span() =>
110 #(#attrs)*
111 #vis #new_sig {
112 let __gl_headless_ctx = {
113 #[allow(unused_mut)]
114 let mut builder = ::gl_headless::_internals::GLContextBuilder::new();
115 #set_version_str
116 builder.build().unwrap()
117 };
118 #item_fn
119 #call
120 }
121 }
122 .into()
123}
124
125#[must_use]
126fn error<T: ToTokens, U: fmt::Display>(tokens: T, message: U) -> TokenStream {
127 syn::Error::new_spanned(tokens, message)
128 .into_compile_error()
129 .into()
130}