@@ -129,7 +129,7 @@ pub fn bind(attr: TokenStream, item: TokenStream) -> TokenStream {
129129 func_name: #func_name_ffi_disp,
130130 func_arguments: #arg_names_disp,
131131 docs: #all_docs,
132- is_variadic : false ,
132+ function_type : :: byondapi :: binds :: FunctionType :: Default ,
133133 }
134134 } ) ;
135135 }
@@ -152,7 +152,7 @@ pub fn bind(attr: TokenStream, item: TokenStream) -> TokenStream {
152152 func_name: #func_name_ffi_disp,
153153 func_arguments: #arg_names_disp,
154154 docs: #all_docs,
155- is_variadic : false ,
155+ function_type : :: byondapi :: binds :: FunctionType :: Default ,
156156 }
157157 } ) ;
158158 }
@@ -286,7 +286,7 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
286286 func_name: #func_name_ffi_disp,
287287 func_arguments: "" ,
288288 docs: #all_docs,
289- is_variadic : true ,
289+ function_type : :: byondapi :: binds :: FunctionType :: Variadic ,
290290 }
291291 } ) ;
292292 }
@@ -309,7 +309,7 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
309309 func_name: #func_name_ffi_disp,
310310 func_arguments: "" ,
311311 docs: #all_docs,
312- is_variadic : true ,
312+ function_type : :: byondapi :: binds :: FunctionType :: Variadic ,
313313 }
314314 } ) ;
315315 }
@@ -352,6 +352,158 @@ pub fn bind_raw_args(attr: TokenStream, item: TokenStream) -> TokenStream {
352352 result. into ( )
353353}
354354
355+ #[ proc_macro_attribute]
356+ pub fn bind_macro ( attr : TokenStream , item : TokenStream ) -> TokenStream {
357+ let input = syn:: parse_macro_input!( item as syn:: ItemFn ) ;
358+ let proc = syn:: parse_macro_input!( attr as Option <syn:: Lit >) ;
359+
360+ let func_name = & input. sig . ident ;
361+ let func_name_disp = quote ! ( #func_name) . to_string ( ) ;
362+
363+ let func_name_ffi = format ! ( "{func_name_disp}_ffi" ) ;
364+ let func_name_ffi = Ident :: new ( & func_name_ffi, func_name. span ( ) ) ;
365+ let func_name_ffi_disp = quote ! ( #func_name_ffi) . to_string ( ) ;
366+
367+ let args = & input. sig . inputs ;
368+
369+ let all_docs = input
370+ . attrs
371+ . iter ( )
372+ . filter ( |attr| matches ! ( attr. style, syn:: AttrStyle :: Outer ) )
373+ . filter_map ( |attr| match & attr. meta {
374+ syn:: Meta :: NameValue ( nameval) => {
375+ let ident = nameval. path . get_ident ( ) ?;
376+ if * ident == "doc" {
377+ match & nameval. value {
378+ syn:: Expr :: Lit ( literal) => match & literal. lit {
379+ syn:: Lit :: Str ( docstring) => {
380+ Some ( format ! ( "///{}\n " , docstring. value( ) , ) )
381+ }
382+ _ => None ,
383+ } ,
384+ _ => None ,
385+ }
386+ } else {
387+ None
388+ }
389+ }
390+ _ => None ,
391+ } )
392+ . collect :: < String > ( ) ;
393+
394+ //Check for returns
395+ let func_return = match & input. sig . output {
396+ syn:: ReturnType :: Default => {
397+ return syn:: Error :: new (
398+ input. span ( ) ,
399+ "Empty returns are not allowed, please return a Result" ,
400+ )
401+ . to_compile_error ( )
402+ . into ( )
403+ }
404+
405+ syn:: ReturnType :: Type ( _, ty) => match ty. as_ref ( ) {
406+ & syn:: Type :: Path ( _) => & input. sig . output ,
407+ _ => {
408+ return syn:: Error :: new ( input. span ( ) , "Invalid return type, please return a Result" )
409+ . to_compile_error ( )
410+ . into ( )
411+ }
412+ } ,
413+ } ;
414+
415+ let signature = quote ! {
416+ #[ no_mangle]
417+ pub unsafe extern "C-unwind" fn #func_name_ffi (
418+ __argc: :: byondapi:: sys:: u4c,
419+ __argv: * mut :: byondapi:: value:: ByondValue
420+ ) -> :: byondapi:: value:: ByondValue
421+ } ;
422+
423+ let body = & input. block ;
424+ let mut arg_names: syn:: punctuated:: Punctuated < syn:: Ident , syn:: Token ![ , ] > =
425+ syn:: punctuated:: Punctuated :: new ( ) ;
426+ let mut proc_arg_unpacker: syn:: punctuated:: Punctuated <
427+ proc_macro2:: TokenStream ,
428+ syn:: Token ![ , ] ,
429+ > = syn:: punctuated:: Punctuated :: new ( ) ;
430+
431+ for arg in args. iter ( ) . map ( extract_args) {
432+ if let syn:: Pat :: Ident ( p) = & * arg. pat {
433+ arg_names. push ( p. ident . clone ( ) ) ;
434+ let index = arg_names. len ( ) - 1 ;
435+ proc_arg_unpacker. push ( quote ! {
436+ args. get( #index) . map( :: byondapi:: value:: ByondValue :: clone) . unwrap_or_default( )
437+ } ) ;
438+ }
439+ }
440+
441+ let arg_names_disp = quote ! ( #arg_names) . to_string ( ) ;
442+
443+ //Submit to inventory
444+ let cthook_prelude = match & proc {
445+ Some ( something) => {
446+ return syn:: Error :: new (
447+ something. span ( ) ,
448+ "Function rename is not supported for macros" ,
449+ )
450+ . to_compile_error ( )
451+ . into ( )
452+ }
453+ None => {
454+ let func_name_disp = func_name_disp. clone ( ) ;
455+ quote ! {
456+ :: byondapi:: inventory:: submit!( {
457+ :: byondapi:: binds:: Bind {
458+ proc_path: #func_name_disp,
459+ func_name: #func_name_ffi_disp,
460+ func_arguments: #arg_names_disp,
461+ docs: #all_docs,
462+ function_type: :: byondapi:: binds:: FunctionType :: Macro ,
463+ }
464+ } ) ;
465+ }
466+ }
467+ } ;
468+
469+ let crash_syntax = if cfg ! ( feature = "old-crash-workaround" ) {
470+ quote ! {
471+ let error_string = :: byondapi:: value:: ByondValue :: try_from( error_string) . unwrap( ) ;
472+ :: byondapi:: global_call:: call_global_id( {
473+ static STACK_TRACE : :: std:: sync:: OnceLock <u32 > = :: std:: sync:: OnceLock :: new( ) ;
474+ * STACK_TRACE . get_or_init( || :: byondapi:: byond_string:: str_id_of( "byondapi_stack_trace" )
475+ . expect( "byondapi-rs implicitly expects byondapi_stack_trace to exist as a proc for error reporting purposes, this proc doesn't exist!" )
476+ )
477+ }
478+ , & [ error_string] ) . unwrap( ) ;
479+ :: byondapi:: value:: ByondValue :: null( )
480+ }
481+ } else {
482+ quote ! {
483+ unsafe { :: byondapi:: runtime:: byond_runtime( error_string) }
484+ }
485+ } ;
486+
487+ let result = quote ! {
488+ #cthook_prelude
489+ #signature {
490+ let args = unsafe { :: byondapi:: parse_args( __argc, __argv) } ;
491+ match #func_name( #proc_arg_unpacker) {
492+ Ok ( val) => val,
493+ Err ( e) => {
494+ let error_string = :: std:: format!( "{e:?}" ) ;
495+ :: std:: mem:: drop( e) ;
496+ #crash_syntax
497+ }
498+ }
499+
500+ }
501+ fn #func_name( #args) #func_return
502+ #body
503+ } ;
504+ result. into ( )
505+ }
506+
355507#[ proc_macro_attribute]
356508pub fn init ( _: TokenStream , item : TokenStream ) -> TokenStream {
357509 let input = syn:: parse_macro_input!( item as syn:: ItemFn ) ;
0 commit comments