Packages
Typed distributed messaging for Gleam on the BEAM.
Retired package: Deprecated - The project needs to be redesigned around a much smaller and clearer core.
Current section
Files
Jump to
Current section
Files
src/rpc_ffi.erl
%% RPC FFI for Gleam distribute library
-module(rpc_ffi).
-export([to_atom/1, is_badrpc/1, get_badrpc_reason/1, to_dynamic/1, call_with_timeout/5, call_binary_with_timeout/5]).
%% Import shared utility for safe atom conversion
-import(distribute_ffi_utils, [to_atom_safe/1]).
%% Safe atom conversion that respects the allow_atom_creation setting.
%% Returns the atom directly (unwrapped) for backward compatibility with RPC calls.
%% NOTE: For RPC, we need actual atoms for module/function names, so we unwrap
%% the result and fall back to creating the atom if needed for valid Erlang identifiers.
to_atom(Input) ->
case to_atom_safe(Input) of
{ok, Atom} -> Atom;
{error, _} ->
%% For RPC module/function names, we need to create atoms.
%% This is safe because these come from code, not user input.
%% If this is truly user input, the caller should validate first.
case Input of
Bin when is_binary(Bin) -> binary_to_atom(Bin, utf8);
List when is_list(List) -> list_to_atom(List);
Atom when is_atom(Atom) -> Atom
end
end.
is_badrpc({badrpc, _Reason}) -> true;
is_badrpc(_) -> false.
get_badrpc_reason({badrpc, Reason}) when is_binary(Reason) -> Reason;
get_badrpc_reason({badrpc, Reason}) when is_atom(Reason) -> atom_to_binary(Reason, utf8);
get_badrpc_reason({badrpc, Reason}) -> iolist_to_binary(io_lib:format("~p", [Reason]));
get_badrpc_reason(_) -> <<"unknown_error">>.
to_dynamic(Value) -> Value.
%% Call with timeout wrapper for RPC: call with timeout in milliseconds
call_with_timeout(Node, Module, Function, Args, TimeoutMs) ->
try
rpc:call(Node, Module, Function, Args, TimeoutMs)
catch
Class:Reason -> {badrpc, iolist_to_binary(io_lib:format("~p:~p", [Class, Reason]))}
end.
%% Call with timeout wrapper for RPC that expects binary results
%% Used for typed RPC where result must be a binary (envelope-wrapped)
call_binary_with_timeout(Node, Module, Function, Args, TimeoutMs) ->
try
case rpc:call(Node, Module, Function, Args, TimeoutMs) of
{badrpc, _} = BadRpc -> BadRpc;
Result when is_binary(Result) -> {ok, Result};
Result -> {error, iolist_to_binary(io_lib:format("expected binary, got: ~p", [Result]))}
end
catch
Class:Reason -> {badrpc, iolist_to_binary(io_lib:format("~p:~p", [Class, Reason]))}
end.