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/distribute@registry.erl
-module(distribute@registry).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/distribute/registry.gleam").
-export([register/2, register_typed/2, register_subject/2, unregister/1, whereis/1, whereis_global/3, whereis_with_tag/2, whereis_typed/1]).
-export_type([register_error/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type register_error() :: already_registered |
invalid_process |
{invalid_name, binary()} |
{network_error, binary()} |
{register_failed, binary()}.
-file("src/distribute/registry.gleam", 73).
?DOC(" Convert RegisterError to string for logging\n").
-spec classify_register_error_to_string(register_error()) -> binary().
classify_register_error_to_string(Error) ->
case Error of
already_registered ->
<<"already_registered"/utf8>>;
invalid_process ->
<<"invalid_process"/utf8>>;
{invalid_name, Msg} ->
<<"invalid_name: "/utf8, Msg/binary>>;
{network_error, Msg@1} ->
<<"network_error: "/utf8, Msg@1/binary>>;
{register_failed, Msg@2} ->
<<"register_failed: "/utf8, Msg@2/binary>>
end.
-file("src/distribute/registry.gleam", 84).
?DOC(" Check if error is network-related\n").
-spec is_network_error(binary()) -> boolean().
is_network_error(Reason) ->
(gleam_stdlib:contains_string(Reason, <<"partition"/utf8>>) orelse gleam_stdlib:contains_string(
Reason,
<<"network"/utf8>>
))
orelse gleam_stdlib:contains_string(Reason, <<"connection"/utf8>>).
-file("src/distribute/registry.gleam", 59).
?DOC(" Classify error reason into structured RegisterError\n").
-spec classify_register_error(binary()) -> register_error().
classify_register_error(Reason) ->
case Reason of
<<"already_registered"/utf8>> ->
already_registered;
<<"invalid_process"/utf8>> ->
invalid_process;
<<"invalid_name"/utf8>> ->
{invalid_name, <<"Name contains invalid characters"/utf8>>};
_ ->
case is_network_error(Reason) of
true ->
{network_error, Reason};
false ->
{register_failed, Reason}
end
end.
-file("src/distribute/registry.gleam", 91).
?DOC(" Validate registry name\n").
-spec validate_name(binary()) -> {ok, nil} | {error, register_error()}.
validate_name(Name) ->
case string:length(Name) of
0 ->
{error, {invalid_name, <<"Name cannot be empty"/utf8>>}};
Len when Len > 255 ->
{error, {invalid_name, <<"Name too long (max 255 chars)"/utf8>>}};
_ ->
case gleam_stdlib:contains_string(Name, <<" "/utf8>>) of
true ->
{error,
{invalid_name, <<"Name cannot contain spaces"/utf8>>}};
false ->
{ok, nil}
end
end.
-file("src/distribute/registry.gleam", 134).
?DOC(" Register a process globally under the given name.\n").
-spec register(binary(), gleam@erlang@process:pid_()) -> {ok, nil} |
{error, register_error()}.
register(Name, Pid) ->
distribute@log:debug(
<<"Registering global name"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
case validate_name(Name) of
{error, E} ->
distribute@log:warn(
<<"Name validation failed during registration"/utf8>>,
[{<<"name"/utf8>>, Name},
{<<"error"/utf8>>, classify_register_error_to_string(E)}]
),
{error, E};
{ok, _} ->
Res = registry_ffi:register(Name, Pid),
case registry_ffi:is_ok_atom(Res) of
true ->
distribute@log:info(
<<"Successfully registered global name"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
{ok, nil};
false ->
case registry_ffi:is_already_registered(Res) of
true ->
distribute@log:warn(
<<"Name already registered"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
{error, already_registered};
false ->
Error = classify_register_error(
registry_ffi:get_error_reason(Res)
),
distribute@log:error(
<<"Failed to register global name"/utf8>>,
[{<<"name"/utf8>>, Name},
{<<"error"/utf8>>,
classify_register_error_to_string(Error)}]
),
{error, Error}
end
end
end.
-file("src/distribute/registry.gleam", 114).
?DOC(
" Register a typed Subject globally.\n"
"\n"
" This registers the owner Pid of the subject.\n"
"\n"
" Recommended: Use global.GlobalSubject and register it with this function.\n"
" The GlobalSubject pattern ensures type-safe messaging with encoder/decoder.\n"
"\n"
" For GlobalSubject: Create with global.new(encoder, decoder), then register\n"
" with register_typed(name, global.subject(global_subject)).\n"
"\n"
" For custom Subject: Works but requires clients to know the message format.\n"
).
-spec register_typed(binary(), gleam@erlang@process:subject(any())) -> {ok, nil} |
{error, register_error()}.
register_typed(Name, Subject) ->
case gleam@erlang@process:subject_owner(Subject) of
{ok, Pid} ->
register(Name, Pid);
{error, nil} ->
{error, invalid_process}
end.
-file("src/distribute/registry.gleam", 126).
?DOC(" Register a Subject globally (alias for register_typed).\n").
-spec register_subject(binary(), gleam@erlang@process:subject(any())) -> {ok,
nil} |
{error, register_error()}.
register_subject(Name, Subject) ->
register_typed(Name, Subject).
-file("src/distribute/registry.gleam", 172).
?DOC(" Unregister a globally registered name.\n").
-spec unregister(binary()) -> {ok, nil} | {error, register_error()}.
unregister(Name) ->
Res = registry_ffi:unregister(Name),
case registry_ffi:is_ok_atom(Res) of
true ->
{ok, nil};
false ->
{error, classify_register_error(registry_ffi:get_error_reason(Res))}
end.
-file("src/distribute/registry.gleam", 182).
?DOC(
" Look up a globally registered process by name.\n"
" Returns Ok(pid) if found, Error(Nil) otherwise.\n"
).
-spec whereis(binary()) -> {ok, gleam@erlang@process:pid_()} | {error, nil}.
whereis(Name) ->
distribute@log:debug(
<<"Resolving global name"/utf8>>,
[{<<"name"/utf8>>, Name}]
),
Res = registry_ffi:whereis(Name),
case registry_ffi:is_pid(Res) of
true ->
{ok, registry_ffi:dynamic_to_pid(Res)};
false ->
{error, nil}
end.
-file("src/distribute/registry.gleam", 198).
?DOC(
" Look up a globally registered GlobalSubject (RECOMMENDED).\n"
" \n"
" This is the type-safe way to lookup distributed processes.\n"
" Returns a GlobalSubject that enforces encoder/decoder usage.\n"
"\n"
" On success, returns a GlobalSubject that can send/receive typed messages.\n"
" On error, returns Error(Nil) if the name is not registered.\n"
).
-spec whereis_global(
binary(),
fun((ILA) -> {ok, bitstring()} | {error, distribute@codec:encode_error()}),
fun((bitstring()) -> {ok, ILA} | {error, distribute@codec:decode_error()})
) -> {ok, distribute@global:global_subject(ILA)} | {error, nil}.
whereis_global(Name, Encoder, Decoder) ->
case whereis(Name) of
{ok, Pid} ->
{ok, distribute@global:from_pid(Pid, Encoder, Decoder)};
{error, nil} ->
{error, nil}
end.
-file("src/distribute/registry.gleam", 216).
?DOC(
" Look up a globally registered process with explicit tag.\n"
"\n"
" Use this when you know the tag of the remote process (e.g., for custom actors).\n"
" For GlobalSubject, use whereis_global instead (recommended).\n"
"\n"
" The tag parameter should match the tag used by the remote actor.\n"
" For most cases with Nil tags, use dynamic.nil() as the tag.\n"
).
-spec whereis_with_tag(binary(), gleam@dynamic:dynamic_()) -> {ok,
gleam@erlang@process:subject(any())} |
{error, nil}.
whereis_with_tag(Name, Tag) ->
case whereis(Name) of
{ok, Pid} ->
{ok, gleam@erlang@process:unsafely_create_subject(Pid, Tag)};
{error, nil} ->
{error, nil}
end.
-file("src/distribute/registry.gleam", 233).
?DOC(
" Look up a globally registered process and return a typed Subject with Nil tag.\n"
"\n"
" ⚠️ **DEPRECATED**: Use `whereis_global` for GlobalSubject or `whereis_with_tag` \n"
" for standard actors where you know the tag.\n"
"\n"
" The returned Subject has a `Nil` tag and won't work with standard gleam_otp actors.\n"
).
-spec whereis_typed(binary()) -> {ok, gleam@erlang@process:subject(any())} |
{error, nil}.
whereis_typed(Name) ->
whereis_with_tag(Name, gleam@dynamic:nil()).