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@cluster.erl
Raw

src/distribute@cluster.erl

-module(distribute@cluster).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/distribute/cluster.gleam").
-export([start_node/2, connect/1, connect_bool/1, nodes/0, self_node/0, ping/1]).
-export_type([start_error/0, connect_error/0, dynamic_/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 start_error() :: {invalid_node_name, binary()} |
already_started |
cookie_too_long |
{network_error, binary()} |
{system_error, binary()} |
{start_failed, binary()}.
-type connect_error() :: connect_timeout |
node_not_found |
{connect_network_error, binary()} |
connect_ignored.
-type dynamic_() :: any().
-file("src/distribute/cluster.gleam", 86).
?DOC(" Convert StartError to string for logging\n").
-spec classify_start_error_to_string(start_error()) -> binary().
classify_start_error_to_string(Error) ->
case Error of
{invalid_node_name, Msg} ->
<<"invalid_node_name: "/utf8, Msg/binary>>;
already_started ->
<<"already_started"/utf8>>;
cookie_too_long ->
<<"cookie_too_long"/utf8>>;
{network_error, Msg@1} ->
<<"network_error: "/utf8, Msg@1/binary>>;
{system_error, Msg@2} ->
<<"system_error: "/utf8, Msg@2/binary>>;
{start_failed, Msg@3} ->
<<"start_failed: "/utf8, Msg@3/binary>>
end.
-file("src/distribute/cluster.gleam", 98).
?DOC(" Check if error is network-related\n").
-spec is_network_error(binary()) -> boolean().
is_network_error(Reason) ->
(gleam_stdlib:contains_string(Reason, <<"network"/utf8>>) orelse gleam_stdlib:contains_string(
Reason,
<<"eaddrinuse"/utf8>>
))
orelse gleam_stdlib:contains_string(Reason, <<"connection"/utf8>>).
-file("src/distribute/cluster.gleam", 105).
?DOC(" Check if error is system/permission related\n").
-spec is_system_error(binary()) -> boolean().
is_system_error(Reason) ->
(gleam_stdlib:contains_string(Reason, <<"permission"/utf8>>) orelse gleam_stdlib:contains_string(
Reason,
<<"access"/utf8>>
))
orelse gleam_stdlib:contains_string(Reason, <<"enoent"/utf8>>).
-file("src/distribute/cluster.gleam", 68).
?DOC(" Classify error reason into structured StartError\n").
-spec classify_start_error(binary()) -> start_error().
classify_start_error(Reason) ->
case Reason of
<<"already_started"/utf8>> ->
already_started;
<<"invalid_node_name"/utf8>> ->
{invalid_node_name, <<"Node name must contain '@'"/utf8>>};
<<"cookie_too_long"/utf8>> ->
cookie_too_long;
_ ->
case is_network_error(Reason) of
true ->
{network_error, Reason};
false ->
case is_system_error(Reason) of
true ->
{system_error, Reason};
false ->
{start_failed, Reason}
end
end
end.
-file("src/distribute/cluster.gleam", 155).
?DOC(" Validate node name format\n").
-spec validate_node_name(binary()) -> {ok, nil} | {error, start_error()}.
validate_node_name(Name) ->
case gleam_stdlib:contains_string(Name, <<"@"/utf8>>) of
true ->
{ok, nil};
false ->
{error,
{invalid_node_name,
<<"Node name must contain '@' symbol"/utf8>>}}
end.
-file("src/distribute/cluster.gleam", 163).
?DOC(" Validate cookie length\n").
-spec validate_cookie(binary()) -> {ok, nil} | {error, start_error()}.
validate_cookie(Cookie) ->
case string:length(Cookie) > 255 of
true ->
{error, cookie_too_long};
false ->
{ok, nil}
end.
-file("src/distribute/cluster.gleam", 113).
?DOC(
" Start distributed node with the given name and cookie.\n"
" Returns Ok(Nil) on success, Error with specific failure reason.\n"
).
-spec start_node(binary(), binary()) -> {ok, nil} | {error, start_error()}.
start_node(Name, Cookie) ->
distribute@log:info(
<<"Starting distributed node"/utf8>>,
[{<<"node"/utf8>>, Name}]
),
case validate_node_name(Name) of
{error, E} ->
distribute@log:error(
<<"Node name validation failed"/utf8>>,
[{<<"node"/utf8>>, Name},
{<<"error"/utf8>>, classify_start_error_to_string(E)}]
),
{error, E};
{ok, _} ->
case validate_cookie(Cookie) of
{error, E@1} ->
distribute@log:error(
<<"Cookie validation failed"/utf8>>,
[{<<"node"/utf8>>, Name},
{<<"error"/utf8>>,
classify_start_error_to_string(E@1)}]
),
{error, E@1};
{ok, _} ->
Res = cluster_ffi:start_node(Name, Cookie),
case cluster_ffi:is_ok_atom(Res) of
true ->
distribute@log:info(
<<"Node started successfully"/utf8>>,
[{<<"node"/utf8>>, Name}]
),
{ok, nil};
false ->
Error = classify_start_error(
cluster_ffi:get_error_reason(Res)
),
distribute@log:error(
<<"Failed to start node"/utf8>>,
[{<<"node"/utf8>>, Name},
{<<"error"/utf8>>,
classify_start_error_to_string(Error)}]
),
{error, Error}
end
end
end.
-file("src/distribute/cluster.gleam", 172).
?DOC(
" Connect to another distributed node.\n"
" Returns Ok(Nil) if connected, Error with reason otherwise.\n"
).
-spec connect(binary()) -> {ok, nil} | {error, connect_error()}.
connect(Node) ->
Result = cluster_ffi:connect(Node),
case cluster_ffi:is_true(Result) of
true ->
{ok, nil};
false ->
case cluster_ffi:is_ignored(Result) of
true ->
{error, connect_ignored};
false ->
{error, node_not_found}
end
end.
-file("src/distribute/cluster.gleam", 187).
?DOC(
" Connect to another distributed node (legacy API).\n"
" Returns True if connected successfully, False otherwise.\n"
" @deprecated Use connect() which returns Result instead\n"
).
-spec connect_bool(binary()) -> boolean().
connect_bool(Node) ->
cluster_ffi:is_true(cluster_ffi:connect(Node)).
-file("src/distribute/cluster.gleam", 192).
?DOC(" Get the list of currently connected nodes.\n").
-spec nodes() -> list(binary()).
nodes() ->
cluster_ffi:nodes().
-file("src/distribute/cluster.gleam", 197).
?DOC(" Get the name of the current node.\n").
-spec self_node() -> binary().
self_node() ->
cluster_ffi:self_node().
-file("src/distribute/cluster.gleam", 203).
?DOC(
" Ping a remote node to check if it is reachable.\n"
" Returns True if the node responds with pong, False otherwise.\n"
).
-spec ping(binary()) -> boolean().
ping(Node) ->
cluster_ffi:ping(Node).