-module(glame). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([execute/2, dial/5]). -export_type([connection/0]). -type connection() :: {connection, mug:socket(), integer()}. -spec validate_auth_response(glame@packet:packet(), integer()) -> {ok, nil} | {error, glame@errors:error()}. validate_auth_response(Pkt, Auth_id) -> case erlang:element(4, Pkt) of 2 -> case erlang:element(3, Pkt) of -1 -> {error, auth_failed}; _ when erlang:element(3, Pkt) =:= Auth_id -> {ok, nil}; Got -> {error, {wrong_response_id, Auth_id, Got}} end; Got@1 -> {error, {wrong_response_type, 2, Got@1}} end. -spec write(connection(), glame@packet:packet_type(), integer(), binary()) -> {ok, nil} | {error, glame@errors:error()}. write(Conn, Packet_type, Packet_id, Cmd) -> case glame@packet:new(Packet_type, Packet_id, Cmd) of {ok, Pkt} -> Bytes = glame@packet:to_bytes(Pkt), case mug:send(erlang:element(2, Conn), Bytes) of {ok, _} -> {ok, nil}; {error, E} -> {error, {socket_error, E}} end; {error, E@1} -> {error, E@1} end. -spec read(connection()) -> {ok, glame@packet:packet()} | {error, glame@errors:error()}. read(Conn) -> case mug:'receive'(erlang:element(2, Conn), erlang:element(3, Conn)) of {ok, Bytes} -> glame@packet:from_bytes(Bytes); {error, E} -> {error, {socket_error, E}} end. -spec execute(connection(), binary()) -> {ok, binary()} | {error, glame@errors:error()}. execute(Conn, Cmd) -> Cmd_len = gleam@string:length(Cmd), case Cmd_len of 0 -> {error, body_empty}; _ when Cmd_len > 4096 -> {error, {body_too_large, Cmd_len}}; _ -> case write(Conn, server_data_exec_command, 3, Cmd) of {ok, _} -> case read(Conn) of {ok, Pkt} -> case gleam@bit_array:to_string( erlang:element(5, Pkt) ) of {ok, Body} -> {ok, Body}; {error, _} -> {error, {body_not_utf8, erlang:element(5, Pkt)}} end; {error, E} -> {error, E} end; {error, E@1} -> {error, E@1} end end. -spec auth(connection(), binary()) -> {ok, nil} | {error, glame@errors:error()}. auth(Conn, Password) -> case write(Conn, server_data_auth, 2, Password) of {ok, _} -> case read(Conn) of {ok, Pkt} -> case erlang:element(4, Pkt) of 0 -> case read(Conn) of {ok, P} -> validate_auth_response(P, 2); {error, E} -> {error, E} end; _ -> validate_auth_response(Pkt, 2) end; {error, E@1} -> {error, E@1} end; {error, E@2} -> {error, E@2} end. -spec dial( binary(), integer(), binary(), gleam@option:option(integer()), gleam@option:option(integer()) ) -> {ok, connection()} | {error, glame@errors:error()}. dial(Host, Port, Password, Connect_timeout_ms, Rw_timeout_ms) -> Sock_result = begin _pipe = mug:new(Host, Port), _pipe@1 = mug:timeout( _pipe, gleam@option:unwrap(Connect_timeout_ms, 5000) ), mug:connect(_pipe@1) end, case Sock_result of {ok, Sock} -> Conn = {connection, Sock, gleam@option:unwrap(Rw_timeout_ms, 5000)}, case auth(Conn, Password) of {ok, _} -> {ok, Conn}; {error, E} -> {error, E} end; {error, E@1} -> {error, {socket_error, E@1}} end.