-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 ).