ruster_unsafe 0.4.0

Create Erlang NIF modules in Rust using the C NIF API.
%% gen_api

%% api_list
%%
%% This is the API list that corresponds to the list found in erl_nif_api_funcs.h
%% Initial conversion provided by https://github.com/lavrin/erlang-rust-nif/issues/2
%%
%% Functions that are not supported for Rust must still be listed so that
%% the Windows callback struct keeps proper integrity.  Such functions
%% must begin with "dummy."
%%

-type api_opt() ::
    exception              |  % enif_xxx_exception() functions
    getenv                 |  % enif_getenv() functions
    time                   |  % new timer API
    {ulongsize, integer()} |  % number of bytes in a C ulong
    dirty_schedulers       .  % enif_is_on_dirty_scheduler()


version_opts("2.7")  -> [{major,2}, {minor,7} ];                           % erlang 17.3
version_opts("2.8")  -> [{major,2}, {minor,8},  exception];                % erlang 18.0
version_opts("2.9")  -> [{major,2}, {minor,9},  exception, getenv];        % erlang 18.2
version_opts("2.10") -> [{major,2}, {minor,10}, exception, getenv, time];  % erlang 18.3
version_opts(_) ->
    io:format("Unsupported Erlang version.\nPlease report to get this version supported.\n"),
    halt(1).

ulong_opts("4") -> [{ulongsize, 4}];
ulong_opts("8") -> [{ulongsize, 8}].

dirty_scheduler_opts() ->
    case catch erlang:system_info(dirty_cpu_schedulers) of
                                 _X when is_integer(_X) -> [dirty_schedulers];
                                 _ -> []
                             end.


-spec api_list([api_opt()]) -> [term()].
api_list(Opts) -> [
    {"*mut c_void", "enif_priv_data", "arg1: *mut ErlNifEnv"},
    {"*mut c_void", "enif_alloc", "size: size_t"},
    {"", "enif_free", "ptr: *mut c_void"},
    {"c_int", "enif_is_atom", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_is_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_is_ref", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_inspect_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, bin: *mut ErlNifBinary"},
    {"c_int", "enif_alloc_binary", "size: size_t, bin: *mut ErlNifBinary"},
    {"c_int", "enif_realloc_binary", "bin: *mut ErlNifBinary, size: size_t"},
    {"", "enif_release_binary", "bin: *mut ErlNifBinary"},
    {"c_int", "enif_get_int", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_int"},
    {"c_int", "enif_get_ulong", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulong"},
    {"c_int", "enif_get_double", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, dp: *mut c_double"},
    {"c_int", "enif_get_list_cell", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, head: *mut ERL_NIF_TERM, tail: *mut ERL_NIF_TERM"},
    {"c_int", "enif_get_tuple", "env: *mut ErlNifEnv, tpl: ERL_NIF_TERM, arity: *mut c_int, array: *mut *const ERL_NIF_TERM"},
    {"c_int", "enif_is_identical", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"},
    {"c_int", "enif_compare", "lhs: ERL_NIF_TERM, rhs: ERL_NIF_TERM"},
    {"ERL_NIF_TERM", "enif_make_binary", "env: *mut ErlNifEnv, bin: *mut ErlNifBinary"},
    {"ERL_NIF_TERM", "enif_make_badarg", "env: *mut ErlNifEnv"},
    {"ERL_NIF_TERM", "enif_make_int", "env: *mut ErlNifEnv, i: c_int"},
    {"ERL_NIF_TERM", "enif_make_ulong", "env: *mut ErlNifEnv, i: c_ulong"},
    {"ERL_NIF_TERM", "enif_make_double", "env: *mut ErlNifEnv, d: c_double"},
    {"ERL_NIF_TERM", "enif_make_atom", "env: *mut ErlNifEnv, name: *const c_uchar"},
    {"c_int", "enif_make_existing_atom", "env: *mut ErlNifEnv, name: *const c_uchar, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"},

    %{"ERL_NIF_TERM", "enif_make_tuple", "env: *mut ErlNifEnv, cnt: c_uint, ..."},
    %{"ERL_NIF_TERM", "enif_make_list", "env: *mut ErlNifEnv, cnt: c_uint, ..."},
    {"", "dummy_enif_make_tuple", ""}, % Can't support variable arguments
    {"", "dummy_enif_make_list", ""}, % Can't support variable arguments

    {"ERL_NIF_TERM", "enif_make_list_cell", "env: *mut ErlNifEnv, car: ERL_NIF_TERM, cdr: ERL_NIF_TERM"},
    {"ERL_NIF_TERM", "enif_make_string", "env: *mut ErlNifEnv, string: *const c_uchar, arg1: ErlNifCharEncoding"},
    {"ERL_NIF_TERM", "enif_make_ref", "env: *mut ErlNifEnv"},

    %% Skip threading API for now (perhaps forever)
    %% If anybody has a situation where they want to use this API instead of the very fine
    %% Rust API, please tell me.
    %%      {"*mut ErlNifMutex", "enif_mutex_create", "name: *mut c_uchar"},
    %%      {"", "enif_mutex_destroy", "mtx: *mut ErlNifMutex"},
    %%      {"c_int", "enif_mutex_trylock", "mtx: *mut ErlNifMutex"},
    %%      {"", "enif_mutex_lock", "mtx: *mut ErlNifMutex"},
    %%      {"", "enif_mutex_unlock", "mtx: *mut ErlNifMutex"},
    %%      {"*mut ErlNifCond", "enif_cond_create", "name: *mut c_uchar"},
    %%      {"", "enif_cond_destroy", "cnd: *mut ErlNifCond"},
    %%      {"", "enif_cond_signal", "cnd: *mut ErlNifCond"},
    %%      {"", "enif_cond_broadcast", "cnd: *mut ErlNifCond"},
    %%      {"", "enif_cond_wait", "cnd: *mut ErlNifCond, mtx: *mut ErlNifMutex"},
    %%      {"*mut ErlNifRWLock", "enif_rwlock_create", "name: *mut c_uchar"},
    %%      {"", "enif_rwlock_destroy", "rwlck: *mut ErlNifRWLock"},
    %%      {"c_int", "enif_rwlock_tryrlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"", "enif_rwlock_rlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"", "enif_rwlock_runlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"c_int", "enif_rwlock_tryrwlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"", "enif_rwlock_rwlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"", "enif_rwlock_rwunlock", "rwlck: *mut ErlNifRWLock"},
    %%      {"c_int", "enif_tsd_key_create", "name: *mut c_uchar, key: *mut ErlNifTSDKey"},
    %%      {"", "enif_tsd_key_destroy", "key: ErlNifTSDKey"},
    %%      {"", "enif_tsd_set", "key: ErlNifTSDKey, data: *mut c_void"},
    %%      {"*mut c_void", "enif_tsd_get", "key: ErlNifTSDKey"},
    %%      {"*mut ErlNifThreadOpts", "enif_thread_opts_create", "name: *mut c_uchar"},
    %%      {"", "enif_thread_opts_destroy", "opts: *mut ErlNifThreadOpts"},
    %%      {"c_int", "enif_thread_create", "name: *mut c_uchar, tid: *mut ErlNifTid, func: Option<extern \"C\" fn (arg1: *mut c_void) -> *mut c_void>, args: *mut c_void, opts: *mut ErlNifThreadOpts"},
    %%      {"ErlNifTid", "enif_thread_self", ""},
    %%      {"c_int", "enif_equal_tids", "tid1: ErlNifTid, tid2: ErlNifTid"},
    %%      {"", "enif_thread_exit", "resp: *mut c_void"},
    %%      {"c_int", "enif_thread_join", "arg1: ErlNifTid, respp: *mut *mut c_void"},
    {"", "dummy_enif_mutex_create", ""},
    {"", "dummy_enif_mutex_destroy", ""},
    {"", "dummy_enif_mutex_trylock", ""},
    {"", "dummy_enif_mutex_lock", ""},
    {"", "dummy_enif_mutex_unlock", ""},
    {"", "dummy_enif_cond_create", ""},
    {"", "dummy_enif_cond_destroy", ""},
    {"", "dummy_enif_cond_signal", ""},
    {"", "dummy_enif_cond_broadcast", ""},
    {"", "dummy_enif_cond_wait", ""},
    {"", "dummy_enif_rwlock_create", ""},
    {"", "dummy_enif_rwlock_destroy", ""},
    {"", "dummy_enif_rwlock_tryrlock", ""},
    {"", "dummy_enif_rwlock_rlock", ""},
    {"", "dummy_enif_rwlock_runlock", ""},
    {"", "dummy_enif_rwlock_tryrwlock", ""},
    {"", "dummy_enif_rwlock_rwlock", ""},
    {"", "dummy_enif_rwlock_rwunlock", ""},
    {"", "dummy_enif_tsd_key_create", ""},
    {"", "dummy_enif_tsd_key_destroy", ""},
    {"", "dummy_enif_tsd_set", ""},
    {"", "dummy_enif_tsd_get", ""},
    {"", "dummy_enif_thread_opts_create", ""},
    {"", "dummy_enif_thread_opts_destroy", ""},
    {"", "dummy_enif_thread_create", ""},
    {"", "dummy_enif_thread_self", ""},
    {"", "dummy_enif_equal_tids", ""},
    {"", "dummy_enif_thread_exit", ""},
    {"", "dummy_enif_thread_join", ""},

    {"*mut c_void", "enif_realloc", "ptr: *mut c_void, size: size_t"},
    {"", "enif_system_info", "sip: *mut ErlNifSysInfo, si_size: size_t"},

    %{"c_int", "enif_fprintf", "filep: *mut c_void, format: *const c_uchar, ..."},
    {"", "dummy_enif_fprintf", ""}, % Can't support variable arguments

    {"c_int", "enif_inspect_iolist_as_binary", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, bin: *mut ErlNifBinary"},
    {"ERL_NIF_TERM", "enif_make_sub_binary", "arg1: *mut ErlNifEnv, bin_term: ERL_NIF_TERM, pos: size_t, size: size_t"},
    {"c_int", "enif_get_string", "arg1: *mut ErlNifEnv, list: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"},
    {"c_int", "enif_get_atom", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, buf: *mut c_uchar, len: c_uint, arg2: ErlNifCharEncoding"},
    {"c_int", "enif_is_fun", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_is_pid", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_is_port", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_get_uint", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_uint"},
    {"c_int", "enif_get_long", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_long"},
    {"ERL_NIF_TERM", "enif_make_uint", "arg1: *mut ErlNifEnv, i: c_uint"},
    {"ERL_NIF_TERM", "enif_make_long", "arg1: *mut ErlNifEnv, i: c_long"},
    {"ERL_NIF_TERM", "enif_make_tuple_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"},
    {"ERL_NIF_TERM", "enif_make_list_from_array", "arg1: *mut ErlNifEnv, arr: *const ERL_NIF_TERM, cnt: c_uint"},
    {"c_int", "enif_is_empty_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"*mut ErlNifResourceType", "enif_open_resource_type", "arg1: *mut ErlNifEnv, module_str: *const c_uchar, name_str: *const c_uchar, dtor: Option<extern \"C\" fn (arg1: *mut ErlNifEnv, arg2: *mut c_void)>, flags: ErlNifResourceFlags, tried: *mut ErlNifResourceFlags"},
    {"*mut c_void", "enif_alloc_resource", "_type: *mut ErlNifResourceType, size: size_t"},
    {"", "enif_release_resource", "obj: *mut c_void"},
    {"ERL_NIF_TERM", "enif_make_resource", "arg1: *mut ErlNifEnv, obj: *mut c_void"},
    {"c_int", "enif_get_resource", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, _type: *mut ErlNifResourceType, objp: *mut *mut c_void"},
    {"size_t", "enif_sizeof_resource", "obj: *mut c_void"},
    {"*mut c_uchar", "enif_make_new_binary", "arg1: *mut ErlNifEnv, size: size_t, termp: *mut ERL_NIF_TERM"},
    {"c_int", "enif_is_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_is_tuple", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_get_atom_length", "arg1: *mut ErlNifEnv, atom: ERL_NIF_TERM, len: *mut c_uint, arg2: ErlNifCharEncoding"},
    {"c_int", "enif_get_list_length", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, len: *mut c_uint"},
    {"ERL_NIF_TERM", "enif_make_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t"},
    {"c_int", "enif_make_existing_atom_len", "env: *mut ErlNifEnv, name: *const c_uchar, len: size_t, atom: *mut ERL_NIF_TERM, arg1: ErlNifCharEncoding"},
    {"ERL_NIF_TERM", "enif_make_string_len", "env: *mut ErlNifEnv, string: *const c_uchar, len: size_t, arg1: ErlNifCharEncoding"},
    {"*mut ErlNifEnv", "enif_alloc_env", ""},
    {"", "enif_free_env", "env: *mut ErlNifEnv"},
    {"", "enif_clear_env", "env: *mut ErlNifEnv"},
    {"c_int", "enif_send", "env: *mut ErlNifEnv, to_pid: *const ErlNifPid, msg_env: *mut ErlNifEnv, msg: ERL_NIF_TERM"},
    {"ERL_NIF_TERM", "enif_make_copy", "dst_env: *mut ErlNifEnv, src_term: ERL_NIF_TERM"},
    {"*mut ErlNifPid", "enif_self", "caller_env: *mut ErlNifEnv, pid: *mut ErlNifPid"},
    {"c_int", "enif_get_local_pid", "env: *mut ErlNifEnv, arg1: ERL_NIF_TERM, pid: *mut ErlNifPid"},
    {"", "enif_keep_resource", "obj: *mut c_void"},
    {"ERL_NIF_TERM", "enif_make_resource_binary", "arg1: *mut ErlNifEnv, obj: *mut c_void, data: *const c_void, size: size_t"}
    ] ++

    case proplists:get_value(ulongsize, Opts) of
        8 -> [];
        4 ->
            [
                {"c_int", "enif_get_int64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_longlong"},
                {"c_int", "enif_get_uint64", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut c_ulonglong"},
                {"ERL_NIF_TERM", "enif_make_int64", "env: *mut ErlNifEnv, i: c_longlong"},
                {"ERL_NIF_TERM", "enif_make_uint64", "env: *mut ErlNifEnv, i: c_ulonglong"}
            ]

    end ++ [
    {"c_int", "enif_is_exception", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_make_reverse_list", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM, list: *mut ERL_NIF_TERM"},
    {"c_int", "enif_is_number", "arg1: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"*mut c_void", "enif_dlopen", "lib: *const c_uchar, err_handler: Option<extern \"C\" fn (arg1: *mut c_void, arg2: *const c_uchar)>, err_arg: *mut c_void"},
    {"*mut c_void", "enif_dlsym", "handle: *mut c_void, symbol: *const c_uchar, err_handler: Option<extern \"C\" fn (arg1: *mut c_void, arg2: *const c_uchar)>, err_arg: *mut c_void"},
    {"c_int", "enif_consume_timeslice", "arg1: *mut ErlNifEnv, percent: c_int"},
    {"c_int", "enif_is_map", "env: *mut ErlNifEnv, term: ERL_NIF_TERM"},
    {"c_int", "enif_get_map_size", "env: *mut ErlNifEnv, term: ERL_NIF_TERM, size: *mut size_t"},
    {"ERL_NIF_TERM", "enif_make_new_map", "env: *mut ErlNifEnv"},
    {"c_int", "enif_make_map_put", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, value: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
    {"c_int", "enif_get_map_value", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, key: ERL_NIF_TERM, value: *mut ERL_NIF_TERM"},
    {"c_int", "enif_make_map_update", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, value: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
    {"c_int", "enif_make_map_remove", "env: *mut ErlNifEnv, map_in: ERL_NIF_TERM, key: ERL_NIF_TERM, map_out: *mut ERL_NIF_TERM"},
    {"c_int", "enif_map_iterator_create", "env: *mut ErlNifEnv, map: ERL_NIF_TERM, iter: *mut ErlNifMapIterator, entry: ErlNifMapIteratorEntry"},
    {"", "enif_map_iterator_destroy", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
    {"c_int", "enif_map_iterator_is_head", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
    {"c_int", "enif_map_iterator_is_tail", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
    {"c_int", "enif_map_iterator_next", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
    {"c_int", "enif_map_iterator_prev", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator"},
    {"c_int", "enif_map_iterator_get_pair", "env: *mut ErlNifEnv, iter: *mut ErlNifMapIterator, key: *mut ERL_NIF_TERM, value: *mut ERL_NIF_TERM"},
    {"ERL_NIF_TERM", "enif_schedule_nif", "env: *mut ErlNifEnv, fun_name: *const c_uchar, flags:c_int, dtor: Option<extern \"C\" fn(env: *mut ErlNifEnv, argc:c_int, argv:*const ERL_NIF_TERM)>, argc:c_int, argv:*const ERL_NIF_TERM"}
    ] ++


    case proplists:get_bool(exception, Opts) of
        true -> [
            {"c_int", "enif_has_pending_exception", "env: *mut ErlNifEnv, reason: *mut ERL_NIF_TERM"},
            {"ERL_NIF_TERM", "enif_raise_exception", "env: *mut ErlNifEnv, reason: ERL_NIF_TERM"}
        ];
        false -> []
    end ++



    case proplists:get_bool(getenv, Opts) of
        true -> [
            {"c_int", "enif_getenv", "key: *const c_uchar, value: *mut c_uchar, value_size: *mut size_t"}
        ];
        false -> []
    end ++


    case proplists:get_bool(time, Opts) of
        true -> [
            {"ErlNifTime", "enif_monotonic_time", "unit: ErlNifTimeUnit"},
            {"ErlNifTime", "enif_time_offset", "unit: ErlNifTimeUnit"},
            {"ErlNifTime", "enif_convert_time_unit", "time: ErlNifTime, from_unit: ErlNifTimeUnit, to_unit: ErlNifTimeUnit"}
        ];
        false -> []
    end ++


    case proplists:get_bool(dirty_schedulers, Opts) of
        true -> [{"c_int", "enif_is_on_dirty_scheduler", "env: *mut ErlNifEnv"}  ];
        false -> []
    end.



main([UlongSizeT]) -> main([UlongSizeT,"."]);
main([UlongSizeT, OutputDir]) ->
    %% Round up all configuration options
    Version = (catch erlang:system_info(nif_version)),
    Opts = version_opts(Version) ++ ulong_opts(UlongSizeT) ++ dirty_scheduler_opts(),
    %% Generate API list
    Entries = api_list(Opts),
    %% Generate Rust code
    Rust = [
        nif_version_rust(proplists:get_value(major, Opts), proplists:get_value(minor, Opts)),
        api_bindings_rust(erlang:system_info(system_architecture), Entries),
        int64_mappers_rust(proplists:get_value(ulongsize, Opts))
    ],
    %% And write it
    Filename = filename:join(OutputDir, "nif_api.snippet"),
    file:write_file(Filename, Rust),
    ok.

nif_version_rust(Major, Minor) ->
    [io_lib:format("pub const NIF_MAJOR_VERSION: c_int = ~p;\n",   [Major]),
     io_lib:format("pub const NIF_MINOR_VERSION: c_int = ~p;\n\n", [Minor])].

api_bindings_rust("win32", Entries) ->
    [ "#[derive(Copy, Clone)]\n",
      "pub struct TWinDynNifCallbacks {\n",
            [ case Return of
                  "" ->
                      io_lib:format("    ~s: fn(~s),\n",[Name,Params]);
                  _ ->
                      io_lib:format("    ~s: fn(~s) -> ~s,\n",[Name,Params,Return])
              end || {Return,Name,Params} <- Entries],

            "}\n\n",

            % The line below would be the "faithful" reproduction of the NIF Win API, but Rust
            % is currently not allowing statics to be uninitialized (1.3 beta).  Revisit this when
            % RFC911 is implemented (or some other mechanism)
            %"static mut WinDynNifCallbacks:TWinDynNifCallbacks = unsafe{std::mem::uninitialized()};\n\n",

            % The work-around is to use Option.  The problem here is that we have to do an unwrap() for
            % each API call which is extra work.
            "pub static mut WinDynNifCallbacks:Option<TWinDynNifCallbacks> = None;\n\n",

            [ [io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n", [Name, Name]),
               case Return of
                  "" ->
                      io_lib:format("#[inline]\npub fn ~s(~s) {\n    unsafe{(WinDynNifCallbacks.unwrap().~s)(~s)\n}}\n\n",[Name,Params,Name,strip_types_from_params(Params)]);
                  _ ->
                      io_lib:format("#[inline]\npub fn ~s(~s) -> ~s {\n    unsafe{(WinDynNifCallbacks.unwrap().~s)(~s)\n}}\n\n",[Name,Params,Return,Name,strip_types_from_params(Params)])
               end] || {Return,Name,Params} <- Entries, not is_dummy(Name)]
            ];

api_bindings_rust(_Arch, Entries) ->
    [ 
        "extern \"C\" {\n",
             [ [io_lib:format("/// See [~s](http://www.erlang.org/doc/man/erl_nif.html#~s) in the Erlang docs.\n", [Name, Name]),
                case Return of
                   "" ->
                       io_lib:format("pub fn ~s(~s);\n",[Name,Params]);
                   _ ->
                       io_lib:format("pub fn ~s(~s) -> ~s;\n",[Name,Params,Return])
                end] || {Return,Name,Params} <- Entries, not is_dummy(Name)],
             "}\n"
    ].

is_dummy([$d,$u,$m,$m,$y|_]) -> true;
is_dummy(_) -> false.



%% Strip types from Rust function definition, example:
%% "arg1: *mut ErlNifEnv, i: c_uint" -> "arg1, i"
strip_types_from_params(Params) ->
    ParamsCleaned0 = re:replace(Params, " ", ""),  % strip spaces
    ParamsCleaned = re:replace(ParamsCleaned0, "\\(.*\\)", ""), % strip nested function params
    ParamsL = re:split(ParamsCleaned, ","),
    ArgsL = [ re:replace(Param, ":.*", "") || Param <- ParamsL ], % strip type info
    join(ArgsL, ", ").

join(List, Joiner) -> join([], List, Joiner).
join(Acc, [H], _Joiner) -> lists:reverse([H|Acc]);
join(Acc, [H|T], Joiner) -> join([Joiner, H|Acc], T, Joiner).

%% These functions are defined in the API above when sizeof(ulong)==8, or they map to
%% long/ulong functions when sizeof(ulong)==4.
int64_mappers_rust(4) -> join([
    "use libc::c_ulonglong;",
    "use libc::c_longlong;"
    ],
    "\n");

int64_mappers_rust(8) -> join([
    "/// See [enif_make_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_int64) at erlang.org",
    "#[inline]",
    "pub fn enif_make_int64(env: *mut ErlNifEnv, i: i64) -> ERL_NIF_TERM",
    "    { unsafe {enif_make_long(env, i)}}",
    "",
    "/// See [enif_make_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_make_uint64) at erlang.org",
    "#[inline]",
    "pub fn enif_make_uint64(env: *mut ErlNifEnv, i: u64) -> ERL_NIF_TERM",
    "    { unsafe {enif_make_ulong(env, i) }}",
    "",
    "/// See [enif_get_int64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_int64) at erlang.org",
    "#[inline]",
    "pub fn enif_get_int64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut i64) -> c_int",
    "    { unsafe {enif_get_long(env, term, ip) }}",
    "",
    "/// See [enif_get_uint64](http://www.erlang.org/doc/man/erl_nif.html#enif_get_uint64) at erlang.org",
    "#[inline]",
    "pub fn enif_get_uint64(env: *mut ErlNifEnv, term: ERL_NIF_TERM, ip: *mut u64) -> c_int",
    "    { unsafe {enif_get_ulong(env, term, ip) }}"],
    "\n").