-module(grammy). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([get_supervisor/1, new/2, port/2, ip_address_to_string/1, udp_send/4, send_to/4, start/1]). -export_type([udp_server/0, internal_message/1, state/2, builder/2, message/1, connection/0, active_type/0, socket_option/0, socket/0]). -opaque udp_server() :: {udp_server, gleam@erlang@process:subject(gleam@otp@supervisor:message())}. -type internal_message(GAA) :: {internal_user, GAA} | {udp_packet, {integer(), integer(), integer(), integer()}, integer(), bitstring()} | unknown. -opaque state(GAB, GAC) :: {state, socket(), GAB, gleam@dict:dict({{integer(), integer(), integer(), integer()}, integer()}, gleam@erlang@process:subject(message(GAC)))}. -type builder(GAD, GAE) :: {builder, fun(() -> {GAD, gleam@option:option(gleam@erlang@process:selector(GAE))}), integer(), fun((message(GAE), connection(), GAD) -> gleam@otp@actor:next(GAE, GAD))}. -type message(GAF) :: {user, GAF} | {packet, {integer(), integer(), integer(), integer()}, integer(), bitstring()}. -opaque connection() :: {connection, socket()}. -type active_type() :: once. -type socket_option() :: binary | {active, active_type()} | {sndbuf, integer()} | {recbuf, integer()}. -type socket() :: any(). -file("src/grammy.gleam", 19). -spec get_supervisor(udp_server()) -> gleam@erlang@process:subject(gleam@otp@supervisor:message()). get_supervisor(Server) -> erlang:element(2, Server). -file("src/grammy.gleam", 144). -spec udp_message_selector() -> gleam@erlang@process:selector(internal_message(any())). udp_message_selector() -> _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:selecting_record5( _pipe, erlang:binary_to_atom(<<"udp"/utf8>>), fun(_, Ip_address, Port, Message) -> Ip = begin _pipe@1 = begin gleam@dynamic@decode:field( 0, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(A) -> gleam@dynamic@decode:field( 1, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(B) -> gleam@dynamic@decode:field( 2, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(C) -> gleam@dynamic@decode:field( 3, {decoder, fun gleam@dynamic@decode:decode_int/1}, fun(D) -> gleam@dynamic@decode:success( {A, B, C, D} ) end ) end ) end ) end ) end, gleam@dynamic@decode:run(Ip_address, _pipe@1) end, Port@1 = gleam@dynamic@decode:run( Port, {decoder, fun gleam@dynamic@decode:decode_int/1} ), Data = gleam@dynamic@decode:run( Message, {decoder, fun gleam@dynamic@decode:decode_bit_array/1} ), case {Ip, Port@1, Data} of {{ok, Ip@1}, {ok, Port@2}, {ok, Data@1}} -> {udp_packet, Ip@1, Port@2, Data@1}; {_, _, _} -> unknown end end ). -file("src/grammy.gleam", 188). -spec new( fun(() -> {GAV, gleam@option:option(gleam@erlang@process:selector(GAW))}), fun((message(GAW), connection(), GAV) -> gleam@otp@actor:next(GAW, GAV)) ) -> builder(GAV, GAW). new(Init, Handler) -> {builder, Init, 4000, Handler}. -file("src/grammy.gleam", 195). -spec port(builder(GBD, GBE), integer()) -> builder(GBD, GBE). port(Builder, Port) -> _record = Builder, {builder, erlang:element(2, _record), Port, erlang:element(4, _record)}. -file("src/grammy.gleam", 231). -spec ip_address_to_string({integer(), integer(), integer(), integer()}) -> binary(). ip_address_to_string(Ip) -> <<<<<<<<<<<<(erlang:integer_to_binary(erlang:element(1, Ip)))/binary, "."/utf8>>/binary, (erlang:integer_to_binary(erlang:element(2, Ip)))/binary>>/binary, "."/utf8>>/binary, (erlang:integer_to_binary(erlang:element(3, Ip)))/binary>>/binary, "."/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Ip)))/binary>>. -file("src/grammy.gleam", 254). -spec udp_send( socket(), {integer(), integer(), integer(), integer()}, integer(), gleam@bytes_tree:bytes_tree() ) -> {ok, nil} | {error, nil}. udp_send(Socket, Host, Port, Packet) -> grammy_ffi:send(Socket, Host, Port, Packet). -file("src/grammy.gleam", 241). -spec send_to( connection(), {integer(), integer(), integer(), integer()}, integer(), gleam@bytes_tree:bytes_tree() ) -> {ok, nil} | {error, nil}. send_to(Connection, Address, Port, Data) -> grammy_ffi:send(erlang:element(2, Connection), Address, Port, Data). -file("src/grammy.gleam", 23). -spec start(builder(any(), any())) -> {ok, udp_server()} | {error, gleam@otp@actor:start_error()}. start(Builder) -> Ten_megabytes = (10 * 1024) * 1024, Worker = gleam@otp@supervisor:worker( fun(_) -> _pipe@10 = gleam@otp@actor:start_spec( {spec, fun() -> case gen_udp:open( erlang:element(3, Builder), [binary, {active, once}, {sndbuf, Ten_megabytes}, {recbuf, Ten_megabytes}] ) of {error, Reason} -> {failed, <<"Failed to open UDP socket: "/utf8, (gleam@string:inspect(Reason))/binary>>}; {ok, Socket} -> {User_state, User_selector} = (erlang:element( 2, Builder ))(), Message_selector = udp_message_selector(), Selector = case User_selector of {some, User} -> _pipe = User, _pipe@1 = gleam_erlang_ffi:map_selector( _pipe, fun(Field@0) -> {internal_user, Field@0} end ), gleam_erlang_ffi:merge_selector( _pipe@1, Message_selector ); none -> Message_selector end, {ready, {state, Socket, User_state, maps:new()}, Selector} end end, 500, fun(Message, State) -> Conn = {connection, erlang:element(2, State)}, case Message of unknown -> logging:log( warning, <<"Discarding unknown message type"/utf8>> ), gleam@otp@actor:continue(State); {udp_packet, Address, Port, Data} -> Resp = (erlang:element(4, Builder))( {packet, Address, Port, Data}, Conn, erlang:element(3, State) ), case Resp of {continue, New_state, User_selector@1} -> _pipe@2 = erlang:element(2, State), _pipe@3 = grammy_ffi:set_active(_pipe@2), _pipe@6 = gleam@result:map( _pipe@3, fun(_) -> Selector@2 = gleam@option:map( User_selector@1, fun(Selector@1) -> _pipe@4 = Selector@1, _pipe@5 = gleam_erlang_ffi:map_selector( _pipe@4, fun(Field@0) -> {internal_user, Field@0} end ), gleam_erlang_ffi:merge_selector( _pipe@5, udp_message_selector( ) ) end ), {continue, begin _record = State, {state, erlang:element( 2, _record ), New_state, erlang:element( 4, _record )} end, Selector@2} end ), _pipe@7 = gleam@result:map_error( _pipe@6, fun(Err) -> logging:log( error, <<"Failed to set UDP socket active: "/utf8, (gleam@string:inspect( Err ))/binary>> ), {stop, {abnormal, <<"Failed to set UDP socket active"/utf8>>}} end ), gleam@result:unwrap_both(_pipe@7); {stop, Reason@1} -> {stop, Reason@1} end; {internal_user, User@1} -> case (erlang:element(4, Builder))( {user, User@1}, Conn, erlang:element(3, State) ) of {continue, New_state@1, none} -> gleam@otp@actor:continue( begin _record@1 = State, {state, erlang:element(2, _record@1), New_state@1, erlang:element(4, _record@1)} end ); {continue, New_state@2, {some, New_user_selector}} -> {continue, begin _record@2 = State, {state, erlang:element(2, _record@2), New_state@2, erlang:element(4, _record@2)} end, {some, begin _pipe@8 = New_user_selector, _pipe@9 = gleam_erlang_ffi:map_selector( _pipe@8, fun(Field@0) -> {internal_user, Field@0} end ), gleam_erlang_ffi:merge_selector( _pipe@9, udp_message_selector() ) end}}; {stop, Reason@2} -> {stop, Reason@2} end end end} ), gleam@result:map( _pipe@10, fun(Subj) -> gleam_stdlib:println( <<"UDP socket listening on port "/utf8, (erlang:integer_to_binary( erlang:element(3, Builder) ))/binary>> ), Subj end ) end ), _pipe@11 = gleam@otp@supervisor:start( fun(Children) -> gleam@otp@supervisor:add(Children, Worker) end ), gleam@result:map(_pipe@11, fun(Field@0) -> {udp_server, Field@0} end).