Current section
Files
Jump to
Current section
Files
src/barnacle.erl
-module(barnacle).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([with_name/2, with_poll_interval/2, with_listener/2, with_connect_nodes_function/2, with_disconnect_nodes_function/2, with_list_nodes_function/2, refresh/2, pause/2, shutdown/2, run_once/1, start/1, child_spec/2, new_strategy/1, custom/1, local_epmd/0, epmd/1, dns/3, get_node_basename/1]).
-export_type([barnacle/1, strategy/1, node_disconnect_error/0, message/1, barnacle_response/1, state/1, refresh_error/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque barnacle(HJY) :: {barnacle,
gleam@option:option(gleam@erlang@atom:atom_()),
strategy(HJY),
integer(),
gleam@option:option(gleam@erlang@process:subject(barnacle_response(HJY)))}.
-opaque strategy(HJZ) :: {strategy,
fun(() -> {ok, list(gleam@erlang@atom:atom_())} | {error, HJZ}),
fun((list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error,
list({gleam@erlang@atom:atom_(),
gleam@erlang@node:connect_error()})}),
fun((list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error, list({gleam@erlang@atom:atom_(), node_disconnect_error()})}),
fun(() -> {ok, list(gleam@erlang@atom:atom_())} | {error, HJZ})}.
-type node_disconnect_error() :: failed_to_disconnect | local_node_is_not_alive.
-opaque message(HKA) :: {refresh,
gleam@option:option(gleam@erlang@process:subject({ok,
list(gleam@erlang@atom:atom_())} |
{error, refresh_error(HKA)}))} |
{pause, gleam@option:option(gleam@erlang@process:subject(nil))} |
{shutdown, gleam@option:option(gleam@erlang@process:subject(nil))}.
-type barnacle_response(HKB) :: {refresh_response,
{ok, list(gleam@erlang@atom:atom_())} | {error, refresh_error(HKB)}} |
{pause_response, nil} |
{shutdown_response, nil}.
-type state(HKC) :: {state,
gleam@erlang@process:subject(message(HKC)),
barnacle(HKC),
gleam@option:option(gleam@erlang@process:timer())}.
-type refresh_error(HKD) :: {strategy_error, HKD} |
{connect_error,
list({gleam@erlang@atom:atom_(), gleam@erlang@node:connect_error()})} |
{disconnect_error,
list({gleam@erlang@atom:atom_(), node_disconnect_error()})}.
-file("src/barnacle.gleam", 38).
?DOC(" Give the barnacle actor process a custom name.\n").
-spec with_name(barnacle(HKL), binary()) -> barnacle(HKL).
with_name(Barnacle, Name) ->
_record = Barnacle,
{barnacle,
{some,
begin
_pipe = Name,
erlang:binary_to_atom(_pipe)
end},
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 43).
?DOC(" Set the poll interval for the barnacle actor. Defaults to 5000ms.\n").
-spec with_poll_interval(barnacle(HKO), integer()) -> barnacle(HKO).
with_poll_interval(Barnacle, Poll_interval) ->
_record = Barnacle,
{barnacle,
erlang:element(2, _record),
erlang:element(3, _record),
Poll_interval,
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 52).
?DOC(
" Set the listener subject for your barnacle. This will be notified whenever\n"
" the barnacle refreshes, is paused, or is shutdown.\n"
).
-spec with_listener(
barnacle(HKR),
gleam@erlang@process:subject(barnacle_response(HKR))
) -> barnacle(HKR).
with_listener(Barnacle, Listener) ->
_record = Barnacle,
{barnacle,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
{some, Listener}}.
-file("src/barnacle.gleam", 98).
?DOC(
" Add a custom node connection function to your strategy.\n"
" Useful if you want to use an alternative to Distributed Erlang, such as\n"
" [Partisan](https://github.com/lasp-lang/partisan).\n"
).
-spec with_connect_nodes_function(
strategy(HLD),
fun((list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error,
list({gleam@erlang@atom:atom_(), gleam@erlang@node:connect_error()})})
) -> strategy(HLD).
with_connect_nodes_function(Strategy, Connect_nodes) ->
_record = Strategy,
{strategy,
erlang:element(2, _record),
Connect_nodes,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 109).
?DOC(
" Add a custom node disconnection function to your strategy.\n"
" Useful if you want to use an alternative to Distributed Erlang, such as\n"
" [Partisan](https://github.com/lasp-lang/partisan).\n"
).
-spec with_disconnect_nodes_function(
strategy(HLL),
fun((list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error, list({gleam@erlang@atom:atom_(), node_disconnect_error()})})
) -> strategy(HLL).
with_disconnect_nodes_function(Strategy, Disconnect_nodes) ->
_record = Strategy,
{strategy,
erlang:element(2, _record),
erlang:element(3, _record),
Disconnect_nodes,
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 120).
?DOC(
" Add a custom node listing function to your strategy.\n"
" Useful if you want to use an alternative to Distributed Erlang, such as\n"
" [Partisan](https://github.com/lasp-lang/partisan).\n"
).
-spec with_list_nodes_function(
strategy(HLT),
fun(() -> {ok, list(gleam@erlang@atom:atom_())} | {error, HLT})
) -> strategy(HLT).
with_list_nodes_function(Strategy, List_nodes) ->
_record = Strategy,
{strategy,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
List_nodes}.
-file("src/barnacle.gleam", 132).
-spec list_nodes() -> {ok, list(gleam@erlang@atom:atom_())} | {error, any()}.
list_nodes() ->
_pipe = [erlang:node() | erlang:nodes()],
_pipe@1 = gleam@list:map(_pipe, fun gleam_erlang_ffi:identity/1),
{ok, _pipe@1}.
-file("src/barnacle.gleam", 284).
?DOC(
" Refresh a barnacle actor. This will attempt to connect to new nodes, and\n"
" disconnect from nodes that are no longer available.\n"
"\n"
" This will reset the poll timer.\n"
).
-spec refresh(gleam@erlang@process:subject(message(HND)), integer()) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error, refresh_error(HND)}.
refresh(Subject, Timeout) ->
gleam@otp@actor:call(
Subject,
fun(Subj) -> {refresh, {some, Subj}} end,
Timeout
).
-file("src/barnacle.gleam", 289).
?DOC(" Pause a barnacle actor. Sending a [`refresh`](#refresh) will restart the actor.\n").
-spec pause(gleam@erlang@process:subject(message(any())), integer()) -> nil.
pause(Subject, Timeout) ->
gleam@otp@actor:call(
Subject,
fun(Subj) -> {pause, {some, Subj}} end,
Timeout
).
-file("src/barnacle.gleam", 295).
?DOC(
" Shutdown a barnacle actor. This will stop the actor, and stop any future\n"
" refreshes.\n"
).
-spec shutdown(gleam@erlang@process:subject(message(any())), integer()) -> nil.
shutdown(Subject, Timeout) ->
gleam@otp@actor:call(
Subject,
fun(Subj) -> {shutdown, {some, Subj}} end,
Timeout
).
-file("src/barnacle.gleam", 357).
-spec send_response(gleam@option:option(gleam@erlang@process:subject(HOA)), HOA) -> nil.
send_response(Maybe_client, Response) ->
gleam@option:map(
Maybe_client,
fun(_capture) -> gleam@erlang@process:send(_capture, Response) end
),
nil.
-file("src/barnacle.gleam", 362).
-spec cancel_timer(gleam@option:option(gleam@erlang@process:timer())) -> nil.
cancel_timer(Timer) ->
gleam@option:map(Timer, fun gleam@erlang@process:cancel_timer/1),
nil.
-file("src/barnacle.gleam", 367).
-spec refresh_nodes(barnacle(HOE)) -> {ok, list(gleam@erlang@atom:atom_())} |
{error, refresh_error(HOE)}.
refresh_nodes(Barnacle) ->
gleam@result:'try'(
begin
_pipe = (erlang:element(2, erlang:element(3, Barnacle)))(),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {strategy_error, Field@0} end
)
end,
fun(Available_nodes_list) ->
gleam@result:'try'(
begin
_pipe@1 = (erlang:element(5, erlang:element(3, Barnacle)))(),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {strategy_error, Field@0} end
)
end,
fun(Current_nodes_list) ->
Self = begin
_pipe@2 = erlang:node(),
gleam_erlang_ffi:identity(_pipe@2)
end,
Available_nodes = begin
_pipe@3 = Available_nodes_list,
_pipe@4 = gleam@set:from_list(_pipe@3),
gleam@set:delete(_pipe@4, Self)
end,
Current_nodes = begin
_pipe@5 = Current_nodes_list,
_pipe@6 = gleam@set:from_list(_pipe@5),
gleam@set:delete(_pipe@6, Self)
end,
Nodes_to_add = gleam@set:difference(
Available_nodes,
Current_nodes
),
Nodes_to_remove = gleam@set:difference(
Current_nodes,
Available_nodes
),
Connect_results = (erlang:element(
3,
erlang:element(3, Barnacle)
))(
begin
_pipe@7 = Nodes_to_add,
gleam@set:to_list(_pipe@7)
end
),
gleam@result:'try'(
begin
_pipe@8 = Connect_results,
gleam@result:map_error(
_pipe@8,
fun(Field@0) -> {connect_error, Field@0} end
)
end,
fun(_) ->
Disconnect_results = (erlang:element(
4,
erlang:element(3, Barnacle)
))(
begin
_pipe@9 = Nodes_to_remove,
gleam@set:to_list(_pipe@9)
end
),
gleam@result:'try'(
begin
_pipe@10 = Disconnect_results,
gleam@result:map_error(
_pipe@10,
fun(Field@0) -> {disconnect_error, Field@0} end
)
end,
fun(_) ->
_pipe@11 = (erlang:element(
5,
erlang:element(3, Barnacle)
))(),
gleam@result:map_error(
_pipe@11,
fun(Field@0) -> {strategy_error, Field@0} end
)
end
)
end
)
end
)
end
).
-file("src/barnacle.gleam", 275).
?DOC(
" Run the refresh function once only. This will not start the actor, and will\n"
" not do any polling.\n"
"\n"
" In this case, most configuration options are irrelevant and will be ignored.\n"
).
-spec run_once(barnacle(HNA)) -> {ok, list(gleam@erlang@atom:atom_())} |
{error, refresh_error(HNA)}.
run_once(Barnacle) ->
_pipe = Barnacle,
refresh_nodes(_pipe).
-file("src/barnacle.gleam", 306).
-spec handle_message(message(HNP), state(HNP)) -> gleam@otp@actor:next(any(), state(HNP)).
handle_message(Message, State) ->
{state, Self, Barnacle, Timer} = State,
case Message of
{refresh, Return} ->
Refresh_result = refresh_nodes(Barnacle),
cancel_timer(Timer),
Timer@1 = gleam@erlang@process:send_after(
Self,
erlang:element(4, Barnacle),
{refresh, none}
),
send_response(Return, Refresh_result),
send_response(
erlang:element(5, Barnacle),
{refresh_response, Refresh_result}
),
gleam@otp@actor:continue(
begin
_record = State,
{state,
erlang:element(2, _record),
erlang:element(3, _record),
{some, Timer@1}}
end
);
{pause, Return@1} ->
cancel_timer(Timer),
send_response(Return@1, nil),
send_response(erlang:element(5, Barnacle), {pause_response, nil}),
gleam@otp@actor:continue(
begin
_record@1 = State,
{state,
erlang:element(2, _record@1),
erlang:element(3, _record@1),
none}
end
);
{shutdown, Return@2} ->
cancel_timer(Timer),
send_response(Return@2, nil),
send_response(erlang:element(5, Barnacle), {shutdown_response, nil}),
{stop, normal}
end.
-file("src/barnacle.gleam", 335).
-spec spec(
barnacle(HNT),
gleam@option:option(gleam@erlang@process:subject(gleam@erlang@process:subject(message(HNT))))
) -> gleam@otp@actor:spec(state(HNT), message(HNT)).
spec(Barnacle, Parent) ->
{spec,
fun() ->
gleam@option:map(
erlang:element(2, Barnacle),
fun(_capture) ->
gleam_erlang_ffi:register_process(erlang:self(), _capture)
end
),
Self = gleam@erlang@process:new_subject(),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:selecting(
_pipe,
Self,
fun gleam@function:identity/1
)
end,
gleam@option:map(
Parent,
fun(_capture@1) ->
gleam@erlang@process:send(_capture@1, Self)
end
),
Timer = gleam@erlang@process:send_after(
Self,
erlang:element(4, Barnacle),
{refresh, none}
),
{ready, {state, Self, Barnacle, {some, Timer}}, Selector}
end,
10000,
fun handle_message/2}.
-file("src/barnacle.gleam", 253).
?DOC(
" Start a barnacle actor. This will start the actor, and begin polling for\n"
" nodes.\n"
).
-spec start(barnacle(HMR)) -> {ok, gleam@erlang@process:subject(message(HMR))} |
{error, gleam@otp@actor:start_error()}.
start(Barnacle) ->
_pipe = Barnacle,
_pipe@1 = spec(_pipe, none),
gleam@otp@actor:start_spec(_pipe@1).
-file("src/barnacle.gleam", 260).
?DOC(" Create a child spec for your barnacle for use in a supervision tree.\n").
-spec child_spec(
barnacle(HMU),
gleam@erlang@process:subject(gleam@erlang@process:subject(message(HMU)))
) -> gleam@otp@supervisor:child_spec(message(HMU), HZQ, HZQ).
child_spec(Barnacle, Parent) ->
gleam@otp@supervisor:worker(fun(_) -> _pipe = Barnacle,
_pipe@1 = spec(_pipe, {some, Parent}),
gleam@otp@actor:start_spec(_pipe@1) end).
-file("src/barnacle.gleam", 407).
-spec result_apply(list({ok, HOH} | {error, HOI})) -> {ok, list(HOH)} |
{error, list(HOI)}.
result_apply(Results) ->
case gleam@result:partition(Results) of
{Vals, []} ->
{ok, Vals};
{_, Errs} ->
{error, Errs}
end.
-file("src/barnacle.gleam", 138).
-spec connect_nodes(list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error,
list({gleam@erlang@atom:atom_(), gleam@erlang@node:connect_error()})}.
connect_nodes(Nodes) ->
_pipe = Nodes,
_pipe@1 = gleam@list:map(
_pipe,
fun(Node) -> case gleam_erlang_ffi:connect_node(Node) of
{ok, _} ->
{ok, Node};
{error, Err} ->
{error, {Node, Err}}
end end
),
result_apply(_pipe@1).
-file("src/barnacle.gleam", 149).
-spec disconnect_nodes(list(gleam@erlang@atom:atom_())) -> {ok,
list(gleam@erlang@atom:atom_())} |
{error, list({gleam@erlang@atom:atom_(), node_disconnect_error()})}.
disconnect_nodes(Nodes) ->
_pipe = Nodes,
_pipe@1 = gleam@list:map(
_pipe,
fun(Node) -> case barnacle_ffi:disconnect_from_node(Node) of
{ok, _} ->
{ok, Node};
{error, Err} ->
{error, {Node, Err}}
end end
),
result_apply(_pipe@1).
-file("src/barnacle.gleam", 79).
-spec default_strategy() -> strategy(any()).
default_strategy() ->
{strategy,
fun() -> {ok, []} end,
fun connect_nodes/1,
fun disconnect_nodes/1,
fun list_nodes/0}.
-file("src/barnacle.gleam", 28).
-spec default_barnacle() -> barnacle(any()).
default_barnacle() ->
{barnacle, none, default_strategy(), 5000, none}.
-file("src/barnacle.gleam", 89).
?DOC(" Create a new custom strategy with a single function to discover nodes.\n").
-spec new_strategy(
fun(() -> {ok, list(gleam@erlang@atom:atom_())} | {error, HKZ})
) -> strategy(HKZ).
new_strategy(Discover_nodes) ->
_record = default_strategy(),
{strategy,
Discover_nodes,
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 171).
?DOC(
" Create a barnacle with a custom strategy. This requires at least one function\n"
" to discover nodes.\n"
"\n"
" You can also supply your own functions for connecting, disconnecting, and\n"
" listing nodes. If these are not supplied, the default Distributed Erlang\n"
" functions will be used.\n"
"\n"
" Barnacles are generic over their error type, known as the strategy error.\n"
" Your strategy functions may use this to return custom errors.\n"
).
-spec custom(strategy(HMJ)) -> barnacle(HMJ).
custom(Strategy) ->
_record = default_barnacle(),
{barnacle,
erlang:element(2, _record),
Strategy,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 177).
?DOC(
" Create a barnacle that uses the local EPMD to discover nodes. This will discover\n"
" nodes on the same hostname as the current node.\n"
).
-spec local_epmd() -> barnacle(nil).
local_epmd() ->
_record = default_barnacle(),
{barnacle,
erlang:element(2, _record),
begin
_record@1 = default_strategy(),
{strategy,
fun barnacle@local_epmd:discover_nodes/0,
erlang:element(3, _record@1),
erlang:element(4, _record@1),
erlang:element(5, _record@1)}
end,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 189).
?DOC(
" Create a barnacle that connects to a known list of nodes. These may be\n"
" remote nodes, or nodes on the same hostname as the current node.\n"
).
-spec epmd(list(gleam@erlang@atom:atom_())) -> barnacle(nil).
epmd(Nodes) ->
_record = default_barnacle(),
{barnacle,
erlang:element(2, _record),
begin
_record@1 = default_strategy(),
{strategy,
fun() -> {ok, Nodes} end,
erlang:element(3, _record@1),
erlang:element(4, _record@1),
erlang:element(5, _record@1)}
end,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 203).
?DOC(
" Create a barnacle that discovers nodes using DNS. The first argument is the\n"
" basename of the nodes. Currently, Barnacle only supports connecting to nodes\n"
" with the same basename.\n"
"\n"
" The second argument is the hostname to query against. Both A and AAAA records\n"
" will be queried. The final argument is an optional timeout for the DNS\n"
" lookup. This defaults to 5000ms if not supplied.\n"
).
-spec dns(binary(), binary(), gleam@option:option(integer())) -> barnacle(barnacle@dns:lookup_error()).
dns(Basename, Hostname_query, Timeout) ->
_record = default_barnacle(),
{barnacle,
erlang:element(2, _record),
begin
_record@1 = default_strategy(),
{strategy,
fun() ->
barnacle@dns:discover_nodes(
Basename,
Hostname_query,
gleam@option:unwrap(Timeout, 5000)
)
end,
erlang:element(3, _record@1),
erlang:element(4, _record@1),
erlang:element(5, _record@1)}
end,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/barnacle.gleam", 428).
?DOC(
" Get the basename of a node.\n"
"\n"
" Useful for getting the basename of the current node for use with the DNS\n"
" strategy.\n"
"\n"
" Example:\n"
" ```gleam\n"
" import barnacle\n"
" import gleam/erlang/node\n"
"\n"
" pub fn main() {\n"
" let assert Ok(basename) = barnacle.get_node_basename(node.self())\n"
" }\n"
" ```\n"
).
-spec get_node_basename(gleam@erlang@node:node_()) -> {ok, binary()} |
{error, nil}.
get_node_basename(Node) ->
_pipe = Node,
_pipe@1 = gleam_erlang_ffi:identity(_pipe),
_pipe@2 = erlang:atom_to_binary(_pipe@1),
_pipe@3 = gleam@string:split_once(_pipe@2, <<"@"/utf8>>),
gleam@result:map(_pipe@3, fun(Tuple) -> erlang:element(1, Tuple) end).