Current section
Files
Jump to
Current section
Files
src/glubsub.erl
-module(glubsub).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/glubsub.gleam").
-export([destroy_topic/1, broadcast/2, new_topic/0, subscribe/2, unsubscribe/2, get_subscribers/1]).
-export_type([topic/1, message/1, glubsub_error/0, state/1, subscriber/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.
?MODULEDOC(
" This module implements a simple pubsub system using gleam actors.\n"
"\n"
).
-type topic(FFL) :: {topic, gleam@erlang@process:subject(message(FFL))}.
-opaque message(FFM) :: {subscribe,
gleam@erlang@process:subject({ok, nil} | {error, glubsub_error()}),
gleam@erlang@process:subject(FFM)} |
{unsubscribe,
gleam@erlang@process:subject({ok, nil} | {error, glubsub_error()}),
gleam@erlang@process:subject(FFM)} |
{broadcast, FFM} |
{get_subscribers, gleam@erlang@process:subject(list(subscriber(FFM)))} |
{subscriber_down, gleam@erlang@process:down()} |
shutdown.
-opaque glubsub_error() :: already_subscribed |
not_subscribed |
{start_error, gleam@otp@actor:start_error()}.
-type state(FFN) :: {state,
list(subscriber(FFN)),
gleam@erlang@process:selector(message(FFN))}.
-type subscriber(FFO) :: {subscriber,
gleam@erlang@process:subject(FFO),
gleam@erlang@process:monitor()}.
-file("src/glubsub.gleam", 179).
-spec topic_to_subject(topic(FGU)) -> gleam@erlang@process:subject(message(FGU)).
topic_to_subject(Topic) ->
{topic, Subject} = Topic,
Subject.
-file("src/glubsub.gleam", 63).
?DOC(" Destroys the given topic, unsubscribing all clients.\n").
-spec destroy_topic(topic(any())) -> nil.
destroy_topic(Topic) ->
gleam@otp@actor:send(topic_to_subject(Topic), shutdown).
-file("src/glubsub.gleam", 88).
?DOC(" Broadcasts a message to all subscribers of the given topic.\n").
-spec broadcast(topic(FGF), FGF) -> {ok, nil} | {error, nil}.
broadcast(Topic, Message) ->
_pipe = gleam@otp@actor:send(topic_to_subject(Topic), {broadcast, Message}),
{ok, _pipe}.
-file("src/glubsub.gleam", 184).
-spec remove_subscriber(list(subscriber(FGY)), subscriber(FGY)) -> list(subscriber(FGY)).
remove_subscriber(Subscribers, Unsubscriber) ->
gleam@list:filter(Subscribers, fun(Sub) -> Sub /= Unsubscriber end).
-file("src/glubsub.gleam", 98).
-spec handle_message(state(FGN), message(FGN)) -> gleam@otp@actor:next(state(FGN), message(FGN)).
handle_message(State, Message) ->
case Message of
{subscribe, Reply, Client} ->
case gleam@list:find(
erlang:element(2, State),
fun(Sub) -> erlang:element(2, Sub) =:= Client end
) of
{ok, _} ->
gleam@otp@actor:send(Reply, {error, already_subscribed}),
gleam@otp@actor:continue(State);
{error, nil} ->
case gleam@erlang@process:subject_owner(Client) of
{ok, Pid} ->
Monitor = gleam@erlang@process:monitor(Pid),
New_subs = [{subscriber, Client, Monitor} |
erlang:element(2, State)],
gleam@otp@actor:send(Reply, {ok, nil}),
gleam@otp@actor:continue(
begin
_record = State,
{state,
New_subs,
erlang:element(3, _record)}
end
);
{error, _} ->
gleam@otp@actor:continue(State)
end
end;
{unsubscribe, Reply@1, Client@1} ->
case gleam@list:find(
erlang:element(2, State),
fun(Sub@1) -> erlang:element(2, Sub@1) =:= Client@1 end
) of
{error, nil} ->
gleam@otp@actor:send(Reply@1, {error, not_subscribed}),
gleam@otp@actor:continue(State);
{ok, Unsub} ->
New_subs@1 = remove_subscriber(
erlang:element(2, State),
Unsub
),
gleam@otp@actor:send(Reply@1, {ok, nil}),
gleam@otp@actor:continue(
begin
_record@1 = State,
{state, New_subs@1, erlang:element(3, _record@1)}
end
)
end;
{broadcast, Message@1} ->
_pipe = erlang:element(2, State),
gleam@list:each(
_pipe,
fun(Sub@2) ->
gleam@otp@actor:send(erlang:element(2, Sub@2), Message@1)
end
),
gleam@otp@actor:continue(State);
{get_subscribers, Reply@2} ->
gleam@otp@actor:send(Reply@2, erlang:element(2, State)),
gleam@otp@actor:continue(State);
{subscriber_down, {process_down, _, Pid@1, _}} ->
Ok_pid = {ok, Pid@1},
case begin
_pipe@1 = erlang:element(2, State),
gleam@list:find(
_pipe@1,
fun(Sub@3) ->
gleam@erlang@process:subject_owner(
erlang:element(2, Sub@3)
)
=:= Ok_pid
end
)
end of
{error, nil} ->
gleam@otp@actor:continue(State);
{ok, Unsub@1} ->
gleam@erlang@process:demonitor_process(
erlang:element(3, Unsub@1)
),
New_subs@2 = remove_subscriber(
erlang:element(2, State),
Unsub@1
),
gleam@otp@actor:continue(
begin
_record@2 = State,
{state, New_subs@2, erlang:element(3, _record@2)}
end
)
end;
{subscriber_down, _} ->
gleam@otp@actor:continue(State);
shutdown ->
gleam@otp@actor:stop()
end.
-file("src/glubsub.gleam", 42).
?DOC(" Creates a new topic. Which is a pubsub channel that clients can subscribe to.\n").
-spec new_topic() -> {ok, topic(any())} | {error, glubsub_error()}.
new_topic() ->
_pipe@5 = gleam@otp@actor:new_with_initialiser(
1000,
fun(Subject) ->
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
_pipe@1 = gleam@erlang@process:select(_pipe, Subject),
gleam@erlang@process:select_monitors(
_pipe@1,
fun(Field@0) -> {subscriber_down, Field@0} end
)
end,
_pipe@2 = gleam@otp@actor:initialised({state, [], Selector}),
_pipe@3 = gleam@otp@actor:selecting(_pipe@2, Selector),
_pipe@4 = gleam@otp@actor:returning(_pipe@3, Subject),
{ok, _pipe@4}
end
),
_pipe@6 = gleam@otp@actor:on_message(_pipe@5, fun handle_message/2),
_pipe@7 = gleam@otp@actor:start(_pipe@6),
_pipe@8 = gleam@result:map(
_pipe@7,
fun(Started) ->
{started, _, Subject@1} = Started,
{topic, Subject@1}
end
),
gleam@result:map_error(_pipe@8, fun(Err) -> {start_error, Err} end).
-file("src/glubsub.gleam", 68).
?DOC(" Subscribes the given client to the given topic.\n").
-spec subscribe(topic(FFV), gleam@erlang@process:subject(FFV)) -> {ok, nil} |
{error, glubsub_error()}.
subscribe(Topic, Client) ->
gleam@otp@actor:call(
topic_to_subject(Topic),
1000,
fun(Self) -> {subscribe, Self, Client} end
).
-file("src/glubsub.gleam", 78).
?DOC(" Unsubscribes the given client from the given topic.\n").
-spec unsubscribe(topic(FGA), gleam@erlang@process:subject(FGA)) -> {ok, nil} |
{error, glubsub_error()}.
unsubscribe(Topic, Client) ->
gleam@otp@actor:call(
topic_to_subject(Topic),
1000,
fun(Self) -> {unsubscribe, Self, Client} end
).
-file("src/glubsub.gleam", 94).
?DOC(" Returns a set of all subscribers to the given topic.\n").
-spec get_subscribers(topic(FGJ)) -> list(subscriber(FGJ)).
get_subscribers(Topic) ->
gleam@otp@actor:call(
topic_to_subject(Topic),
1000,
fun(Field@0) -> {get_subscribers, Field@0} end
).