omp-codegen 1.0.0

Codegen for omprs
Documentation
use proc_macro::{Span, TokenStream};
use quote::quote;
use syn::{
    parse::{Parse, ParseStream},
    parse_macro_input, ExprTuple, Ident, ItemFn, Lit, LitStr, Token,
};

struct EntryArgs {
    name: Option<Lit>,
    version: Option<ExprTuple>,
}

impl Parse for EntryArgs {
    fn parse(input: ParseStream) -> Result<Self, syn::Error> {
        let mut name: Option<Lit> = None;
        let mut version: Option<ExprTuple> = None;
        if input.is_empty() {
            return Ok(EntryArgs { name, version });
        }
        let key: Ident = input.parse()?;

        if key == "name" {
            let _: Token![=] = input.parse()?;
            name = Some(input.parse()?);

            if !input.is_empty() {
                let _: Token![,] = input.parse()?;
                let key: Ident = input.parse()?;

                if key == "version" {
                    let _: Token![=] = input.parse()?;
                    version = Some(input.parse()?);
                }
            }
        } else if key == "version" {
            let _: Token![=] = input.parse()?;
            version = Some(input.parse()?);
            if !input.is_empty() {
                let _: Token![,] = input.parse()?;
                let key: Ident = input.parse()?;

                if key == "name" {
                    let _: Token![=] = input.parse()?;
                    name = Some(input.parse()?);
                }
            }
        }

        Ok(EntryArgs { name, version })
    }
}

pub fn create_main(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = parse_macro_input!(args as EntryArgs);

    let component_name = if args.name.is_some() {
        args.name.unwrap()
    } else {
        Lit::from(LitStr::new("OMPRS Gamemode", Span::call_site().into()))
    };

    let component_version = if args.version.is_some() {
        let version = args.version.unwrap();
        quote!(#version)
    } else {
        quote!((0, 0, 0, 0))
    };
    let sig = parse_macro_input!(input as ItemFn);
    let function_name = sig.clone().sig.ident;
    let code = quote! {
        #sig
        #[no_mangle]
        extern "C" fn onReadyCB() {
            let _ = #function_name();
        }

        #[no_mangle]
        extern "C" fn onResetCB() {}

        #[no_mangle]
        extern "C" fn onFreeCB() {}

        #[no_mangle]
        unsafe extern "C" fn ComponentEntryPoint() -> *const std::ffi::c_void {
            omp::init_functions();

            let component = omp::Component_Create.unwrap()(
                omp::gen_uid(),
                std::ffi::CString::new(#component_name).unwrap().into_raw(),
                omp::ComponentVersion {
                    major: #component_version.0,
                    minor: #component_version.1,
                    patch: #component_version.2,
                    prerel: #component_version.3,
                },
                onReadyCB as *const std::ffi::c_void,
                onResetCB as *const std::ffi::c_void,
                onFreeCB as *const std::ffi::c_void,
            );

            component
        }
    };
    code.into()
}