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
distribute src distribute_ffi_utils.erl
Raw

src/distribute_ffi_utils.erl

%% @doc Common utility functions for distribute FFI modules.
%%
%% This module provides shared functionality used across multiple FFI modules
%% to avoid code duplication and ensure consistent behavior.
%%
%% ## Key Functions
%%
%% - `to_atom_safe/1`: Safely convert binaries/lists to atoms respecting
%% the `allow_atom_creation` setting.
%%
%% ## Atom Creation Policy
%%
%% Atom creation is controlled by the `allow_atom_creation` flag in settings.
%% When disabled (default), only existing atoms can be looked up, preventing
%% atom table exhaustion attacks from untrusted input.
%%
%% When enabled, new atoms can be created. Use this only when:
%% - The input is from a trusted source (e.g., configuration)
%% - You've validated the input won't cause unbounded atom creation
%%
%% ## Example Usage
%%
%% ```erlang
%% -import(distribute_ffi_utils, [to_atom_safe/1]).
%%
%% register_name(Name) ->
%% case to_atom_safe(Name) of
%% {ok, Atom} -> {ok, register(Atom, self())};
%% {error, Reason} -> {error, Reason}
%% end.
%% ```
-module(distribute_ffi_utils).
-export([
to_atom_safe/1,
get_allow_atom_creation/0
]).
%% @doc Safely convert a binary, list, or atom to an atom.
%%
%% Respects the `allow_atom_creation` setting from settings_ffi.
%% When allow_atom_creation is false (default), uses binary_to_existing_atom
%% which only succeeds if the atom already exists.
%%
%% When allow_atom_creation is true, uses binary_to_atom which can create
%% new atoms (use with caution to avoid atom table exhaustion).
%%
%% ## Arguments
%%
%% - `Input` - A binary, list (string), or atom
%%
%% ## Returns
%%
%% - `{ok, Atom}` - Successfully converted to atom
%% - `{error, Reason}` - Conversion failed
%% - `<<"badarg">>` - Invalid input type
%% - `<<"atom_not_found">>` - Atom doesn't exist (when creation disabled)
%%
%% ## Examples
%%
%% ```erlang
%% {ok, foo} = to_atom_safe(<<"foo">>). % Existing atom
%% {ok, bar} = to_atom_safe("bar"). % From string
%% {ok, baz} = to_atom_safe(baz). % Already an atom
%% {error, _} = to_atom_safe(123). % Invalid type
%% ```
-spec to_atom_safe(Input :: binary() | list() | atom()) ->
{ok, atom()} | {error, binary()}.
to_atom_safe(Bin) when is_list(Bin) ->
to_atom_safe(list_to_binary(Bin));
to_atom_safe(Bin) when is_binary(Bin) ->
AllowCreation = get_allow_atom_creation(),
try
case AllowCreation of
true -> {ok, binary_to_atom(Bin, utf8)};
false -> {ok, binary_to_existing_atom(Bin, utf8)}
end
catch
_:_ -> {error, <<"atom_not_found">>}
end;
to_atom_safe(Atom) when is_atom(Atom) ->
{ok, Atom};
to_atom_safe(_) ->
{error, <<"badarg">>}.
%% @doc Get the current allow_atom_creation setting.
%%
%% Returns the value from settings_ffi if available, otherwise defaults to false.
%% This is a safe default that prevents atom table exhaustion.
%%
%% ## Returns
%%
%% - `true` - Atom creation is allowed
%% - `false` - Only existing atoms can be looked up (default)
-spec get_allow_atom_creation() -> boolean().
get_allow_atom_creation() ->
try
settings_ffi:get_allow_atom_creation()
catch
_:_ -> false % Default to safe behavior
end.