Current section
Files
Jump to
Current section
Files
src/whisper.erl
-module(whisper).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/whisper.gleam").
-export([on/2, subscribe/1, publish/2, close/1, new_topic/2]).
-export_type([receive_error/0, receive_result/1, subscription/1, topic_error/0, whisper/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.
-type receive_error() :: topic_is_not_registered | subscription_cancelled.
-type receive_result(DVK) :: {message, DVK} | empty | {closed, receive_error()}.
-type subscription(DVL) :: {subscription,
fun(() -> receive_result(DVL)),
fun(() -> nil)}.
-type topic_error() :: invalid_topic | invalid_capacity | already_registered.
-type whisper(DVM) :: {whisper, binary(), integer()} | {gleam_phantom, DVM}.
-file("src/whisper.gleam", 68).
?DOC(
" Registers a listener function that will be called whenever a message is\n"
" published to the topic.\n"
"\n"
" Returns a function that can be called to unregister the listener. Multiple\n"
" listeners can be registered on the same topic, and each will receive all\n"
" published messages.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let close = on(whisper, fn(message) {\n"
" io.println(\"Received: \" <> message)\n"
" })\n"
" // Later, to stop listening:\n"
" close()\n"
" ```\n"
).
-spec on(whisper(DVV), fun((DVV) -> nil)) -> fun(() -> nil).
on(Whisper, Listener) ->
whisper_ffi:on(erlang:element(2, Whisper), Listener).
-file("src/whisper.gleam", 96).
?DOC(
" Creates a subscription that allows pulling messages from the topic on demand.\n"
"\n"
" Unlike `on`, which pushes messages to a callback, `subscribe` returns a\n"
" `Subscription` that can be polled using its `receive` function. The subscription\n"
" maintains its own message queue up to the topic's capacity.\n"
"\n"
" Call the subscription's `cancel` function to stop receiving messages.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let sub = subscribe(whisper)\n"
" case sub.receive() {\n"
" Message(msg) -> io.println(\"Got: \" <> msg)\n"
" Empty -> io.println(\"No messages\")\n"
" Closed(err) -> io.println(\"Subscription closed\")\n"
" }\n"
" // Later, to cancel:\n"
" sub.cancel()\n"
" ```\n"
).
-spec subscribe(whisper(DVY)) -> subscription(DVY).
subscribe(Whisper) ->
{Receive_fn, Cancel_fn} = whisper_ffi:subscribe(erlang:element(2, Whisper)),
{subscription, Receive_fn, Cancel_fn}.
-file("src/whisper.gleam", 116).
?DOC(
" Publishes a message to all listeners and subscribers of the topic.\n"
"\n"
" The message will be queued for each subscription up to the topic's capacity.\n"
" If a subscription's queue is full, older messages may be dropped depending on\n"
" the platform implementation.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" publish(whisper, \"Hello, world!\")\n"
" ```\n"
).
-spec publish(whisper(DWD), DWD) -> nil.
publish(Whisper, Message) ->
whisper_ffi:publish(erlang:element(2, Whisper), Message).
-file("src/whisper.gleam", 135).
?DOC(
" Closes the topic and notifies all active subscriptions.\n"
"\n"
" After closing, subscribers will receive `Closed(TopicIsNotRegistered)` when\n"
" attempting to receive messages, and new subscriptions or listeners cannot be\n"
" created for this topic.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" close(whisper)\n"
" ```\n"
).
-spec close(whisper(any())) -> nil.
close(Whisper) ->
whisper_ffi:close(erlang:element(2, Whisper)).
-file("src/whisper.gleam", 143).
-spec validate_topic(binary()) -> {ok, binary()} | {error, topic_error()}.
validate_topic(Topic) ->
case Topic of
<<""/utf8>> ->
{error, invalid_topic};
_ ->
{ok, Topic}
end.
-file("src/whisper.gleam", 150).
-spec validate_capacity(integer()) -> {ok, integer()} | {error, topic_error()}.
validate_capacity(Capacity) ->
case Capacity of
C when C > 0 ->
{ok, C};
_ ->
{error, invalid_capacity}
end.
-file("src/whisper.gleam", 39).
?DOC(
" Creates a new topic with the specified capacity for message buffering.\n"
"\n"
" The topic name must be non-empty and the capacity must be greater than zero.\n"
" Returns an error if the topic name is invalid, capacity is invalid, or the\n"
" topic is already registered.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(whisper) = new_topic(\"notifications\", capacity: 100)\n"
" ```\n"
).
-spec new_topic(binary(), integer()) -> {ok, whisper(any())} |
{error, topic_error()}.
new_topic(Topic, Capacity) ->
gleam@result:'try'(
validate_topic(Topic),
fun(Topic@1) ->
gleam@result:'try'(
validate_capacity(Capacity),
fun(Capacity@1) -> whisper_ffi:register(Topic@1, Capacity@1) end
)
end
).