-module(mug). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -define(FILEPATH, "src/mug.gleam"). -export([new/2, timeout/2, connect/1, send_builder/2, send/2, 'receive'/2, receive_exact/3, shutdown/1, receive_next_packet_as_message/1, select_tcp_messages/2]). -export_type([socket/0, do_not_leak/0, error/0, connection_options/0, mode_value/0, active_value/0, gen_tcp_option/0, tcp_message/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 socket() :: any(). -type do_not_leak() :: any(). -type error() :: closed | timeout | eaddrinuse | eaddrnotavail | eafnosupport | ealready | econnaborted | econnrefused | econnreset | edestaddrreq | ehostdown | ehostunreach | einprogress | eisconn | emsgsize | enetdown | enetunreach | enopkg | enoprotoopt | enotconn | enotty | enotsock | eproto | eprotonosupport | eprototype | esocktnosupport | etimedout | ewouldblock | exbadport | exbadseq | nxdomain | eacces | eagain | ebadf | ebadmsg | ebusy | edeadlk | edeadlock | edquot | eexist | efault | efbig | eftype | eintr | einval | eio | eisdir | eloop | emfile | emlink | emultihop | enametoolong | enfile | enobufs | enodev | enolck | enolink | enoent | enomem | enospc | enosr | enostr | enosys | enotblk | enotdir | enotsup | enxio | eopnotsupp | eoverflow | eperm | epipe | erange | erofs | espipe | esrch | estale | etxtbsy | exdev. -type connection_options() :: {connection_options, binary(), integer(), integer()}. -type mode_value() :: binary. -type active_value() :: any(). -type gen_tcp_option() :: {active, active_value()} | {mode, mode_value()}. -type tcp_message() :: {packet, socket(), bitstring()} | {socket_closed, socket()} | {tcp_error, socket(), error()}. -file("src/mug.gleam", 118). ?DOC(" Create a new set of connection options.\n"). -spec new(binary(), integer()) -> connection_options(). new(Host, Port) -> {connection_options, Host, Port, 1000}. -file("src/mug.gleam", 124). ?DOC(" Specify a timeout for the connection to be established.\n"). -spec timeout(connection_options(), integer()) -> connection_options(). timeout(Options, Timeout) -> _record = Options, {connection_options, erlang:element(2, _record), erlang:element(3, _record), Timeout}. -file("src/mug.gleam", 141). ?DOC( " Establish a TCP connection to the server specified in the connection\n" " options.\n" "\n" " Returns an error if the connection could not be established.\n" "\n" " The socket is created in passive mode, meaning the the `receive` function is\n" " to be called to receive packets from the client. The\n" " `receive_next_packet_as_message` function can be used to switch the socket\n" " to active mode and receive the next packet as an Erlang message.\n" ). -spec connect(connection_options()) -> {ok, socket()} | {error, error()}. connect(Options) -> Gen_options = [{active, mug_ffi:passive()}, {mode, binary}], Host = unicode:characters_to_list(erlang:element(2, Options)), gen_tcp:connect( Host, erlang:element(3, Options), Gen_options, erlang:element(4, Options) ). -file("src/mug.gleam", 189). ?DOC( " Send a message to the client, the data in `BytesBuilder`. Using this function\n" " is more efficient turning an `BytesBuilder` or a `StringBuilder` into a\n" " `BitArray` to use with the `send` function.\n" ). -spec send_builder(socket(), gleam@bytes_tree:bytes_tree()) -> {ok, nil} | {error, error()}. send_builder(Socket, Packet) -> mug_ffi:send(Socket, Packet). -file("src/mug.gleam", 180). ?DOC(" Send a message to the client.\n"). -spec send(socket(), bitstring()) -> {ok, nil} | {error, error()}. send(Socket, Message) -> mug_ffi:send(Socket, gleam@bytes_tree:from_bit_array(Message)). -file("src/mug.gleam", 196). ?DOC( " Receive a message from the client.\n" "\n" " Errors if the socket is closed, if the timeout is reached, or if there is\n" " some other problem receiving the packet.\n" ). -spec 'receive'(socket(), integer()) -> {ok, bitstring()} | {error, error()}. 'receive'(Socket, Timeout) -> gen_tcp:recv(Socket, 0, Timeout). -file("src/mug.gleam", 213). ?DOC( " Receive the specified number of bytes from the client, unless the socket\n" " was closed, from the other side. In that case, the last read may return\n" " less bytes.\n" " If the specified number of bytes is not available to read from the socket\n" " then the function will block until the bytes are available, or until the\n" " timeout is reached.\n" " This directly calls the underlying Erlang function `gen_tcp:recv/3`.\n" "\n" " Errors if the socket is closed, if the timeout is reached, or if there is\n" " some other problem receiving the packet.\n" ). -spec receive_exact(socket(), integer(), integer()) -> {ok, bitstring()} | {error, error()}. receive_exact(Socket, Size, Timeout) -> gen_tcp:recv(Socket, Size, Timeout). -file("src/mug.gleam", 232). ?DOC( " Close the socket, ensuring that any data buffered in the socket is flushed\n" " to the operating system kernel socket first.\n" ). -spec shutdown(socket()) -> {ok, nil} | {error, error()}. shutdown(Socket) -> mug_ffi:shutdown(Socket). -file("src/mug.gleam", 245). ?DOC( " Switch the socket to active mode, meaning that the next packet received on\n" " the socket will be sent as an Erlang message to the socket owner's inbox.\n" "\n" " This is useful for when you wish to have an OTP actor handle incoming\n" " messages as using the `receive` function would result in the actor being\n" " blocked and unable to handle other messages while waiting for the next\n" " packet.\n" "\n" " Messages will be send to the process that controls the socket, which is the\n" " process that established the socket with the `connect` function.\n" ). -spec receive_next_packet_as_message(socket()) -> nil. receive_next_packet_as_message(Socket) -> inet:setopts(Socket, [{active, mug_ffi:active_once()}]), nil. -file("src/mug.gleam", 284). -spec map_tcp_message(fun((tcp_message()) -> HHI)) -> fun((gleam@dynamic:dynamic_()) -> HHI). map_tcp_message(Mapper) -> fun(Message) -> Mapper(mug_ffi:coerce_tcp_message(Message)) end. -file("src/mug.gleam", 270). ?DOC( " Configure a selector to receive messages from TCP sockets.\n" "\n" " Note this will receive messages from all TCP sockets that the process\n" " controls, rather than any specific one. If you wish to only handle messages\n" " from one socket then use one process per socket.\n" ). -spec select_tcp_messages( gleam@erlang@process:selector(HHF), fun((tcp_message()) -> HHF) ) -> gleam@erlang@process:selector(HHF). select_tcp_messages(Selector, Mapper) -> Tcp = erlang:binary_to_atom(<<"tcp"/utf8>>), Closed = erlang:binary_to_atom(<<"tcp_closed"/utf8>>), Error = erlang:binary_to_atom(<<"tcp_error"/utf8>>), _pipe = Selector, _pipe@1 = gleam@erlang@process:select_record( _pipe, Tcp, 2, map_tcp_message(Mapper) ), _pipe@2 = gleam@erlang@process:select_record( _pipe@1, Closed, 1, map_tcp_message(Mapper) ), gleam@erlang@process:select_record( _pipe@2, Error, 2, map_tcp_message(Mapper) ).