THRIFT-5635 Update erlang client for Erlang 23-25
Client: erl
Patch: Sergey Yelin

This closes #2677

Summary of changes:
 - Add useful compiler options
 - Format sources using erlfmt
 - Switch to modern callbacks in thrift_* modules
 - Add static analysis (dialyzer), disabled by default
 - Add/fix types for API calls

NOTE: Enabling static analysis requires additional tweaks in multiplexer module.
diff --git a/lib/erl/src/thrift_client.erl b/lib/erl/src/thrift_client.erl
index 1a9cb50..1c27405 100644
--- a/lib/erl/src/thrift_client.erl
+++ b/lib/erl/src/thrift_client.erl
@@ -25,78 +25,91 @@
 -include("thrift_constants.hrl").
 -include("thrift_protocol.hrl").
 
--record(tclient, {service, protocol, seqid}).
+-record(tclient, {
+    service :: module(),
+    protocol :: thrift_protocol:state(),
+    seqid :: non_neg_integer()
+}).
+-type tclient() :: #tclient{}.
 
-
-new(Protocol, Service)
-  when is_atom(Service) ->
-    {ok, #tclient{protocol = Protocol,
-                  service = Service,
-                  seqid = 0}}.
+new(Protocol, Service) when
+    is_atom(Service)
+->
+    {ok, #tclient{
+        protocol = Protocol,
+        service = Service,
+        seqid = 0
+    }}.
 
 -spec call(#tclient{}, atom(), list()) -> {#tclient{}, {ok, any()} | {error, any()}}.
-call(Client = #tclient{}, Function, Args)
-when is_atom(Function), is_list(Args) ->
-  case send_function_call(Client, Function, Args) of
-    {ok, Client1} -> receive_function_result(Client1, Function);
-    {{error, X}, Client1} -> {Client1, {error, X}};
-    Else -> Else
-  end.
-
+call(Client = #tclient{}, Function, Args) when
+    is_atom(Function), is_list(Args)
+->
+    case send_function_call(Client, Function, Args) of
+        {ok, Client1} -> receive_function_result(Client1, Function);
+        {{error, X}, Client1} -> {Client1, {error, X}}
+    end.
 
 %% Sends a function call but does not read the result. This is useful
 %% if you're trying to log non-oneway function calls to write-only
 %% transports like thrift_disk_log_transport.
 -spec send_call(#tclient{}, atom(), list()) -> {#tclient{}, ok}.
-send_call(Client = #tclient{}, Function, Args)
-  when is_atom(Function), is_list(Args) ->
+send_call(Client = #tclient{}, Function, Args) when
+    is_atom(Function), is_list(Args)
+->
     case send_function_call(Client, Function, Args) of
-      {ok, Client1} -> {Client1, ok};
-      Else -> Else
+        {ok, Client1} -> {Client1, ok};
+        Else -> Else
     end.
 
 -spec close(#tclient{}) -> ok.
-close(#tclient{protocol=Protocol}) ->
+close(#tclient{protocol = Protocol}) ->
     thrift_protocol:close_transport(Protocol).
 
-
 %%--------------------------------------------------------------------
 %%% Internal functions
 %%--------------------------------------------------------------------
 -spec send_function_call(#tclient{}, atom(), list()) -> {ok | {error, any()}, #tclient{}}.
 send_function_call(Client = #tclient{service = Service}, Function, Args) ->
-  {Params, Reply} = try
-    {Service:function_info(Function, params_type), Service:function_info(Function, reply_type)}
-  catch error:function_clause -> {no_function, 0}
-  end,
-  MsgType = case Reply of
-    oneway_void -> ?tMessageType_ONEWAY;
-    _ -> ?tMessageType_CALL
-  end,
-  case Params of
-    no_function ->
-      {{error, {no_function, Function}}, Client};
-    {struct, PList} when length(PList) =/= length(Args) ->
-      {{error, {bad_args, Function, Args}}, Client};
-    {struct, _PList} -> write_message(Client, Function, Args, Params, MsgType)
-  end.
+    {Params, Reply} =
+        try
+            {
+                Service:function_info(Function, params_type),
+                Service:function_info(Function, reply_type)
+            }
+        catch
+            error:function_clause -> {no_function, 0}
+        end,
+    MsgType =
+        case Reply of
+            oneway_void -> ?tMessageType_ONEWAY;
+            _ -> ?tMessageType_CALL
+        end,
+    case Params of
+        no_function ->
+            {{error, {no_function, Function}}, Client};
+        {struct, PList} when length(PList) =/= length(Args) ->
+            {{error, {bad_args, Function, Args}}, Client};
+        {struct, _PList} ->
+            write_message(Client, Function, Args, Params, MsgType)
+    end.
 
 -spec write_message(#tclient{}, atom(), list(), {struct, list()}, integer()) ->
-  {ok | {error, any()}, #tclient{}}.
+    {ok | {error, any()}, #tclient{}}.
 write_message(Client = #tclient{protocol = P0, seqid = Seq}, Function, Args, Params, MsgType) ->
-  try
-    {P1, ok} = thrift_protocol:write(P0, #protocol_message_begin{
-      name = atom_to_list(Function),
-      type = MsgType,
-      seqid = Seq
-    }),
-    {P2, ok} = thrift_protocol:write(P1, {Params, list_to_tuple([Function|Args])}),
-    {P3, ok} = thrift_protocol:write(P2, message_end),
-    {P4, ok} = thrift_protocol:flush_transport(P3),
-    {ok, Client#tclient{protocol = P4}}
-  catch
-    error:{badmatch, {_, {error, _} = Error}} -> {Error, Client}
-  end.
+    try
+        {P1, ok} = thrift_protocol:write(P0, #protocol_message_begin{
+            name = atom_to_list(Function),
+            type = MsgType,
+            seqid = Seq
+        }),
+        {P2, ok} = thrift_protocol:write(P1, {Params, list_to_tuple([Function | Args])}),
+        {P3, ok} = thrift_protocol:write(P2, message_end),
+        {P4, ok} = thrift_protocol:flush_transport(P3),
+        {ok, Client#tclient{protocol = P4}}
+    catch
+        error:{badmatch, {_, {error, _} = Error}} -> {Error, Client}
+    end.
 
 -spec receive_function_result(#tclient{}, atom()) -> {#tclient{}, {ok, any()} | {error, any()}}.
 receive_function_result(Client = #tclient{service = Service}, Function) ->
@@ -105,32 +118,38 @@
 
 read_result(Client, _Function, oneway_void) ->
     {Client, {ok, ok}};
-
-read_result(Client = #tclient{protocol = Proto0,
-                              seqid    = SeqId},
-            Function,
-            ReplyType) ->
+read_result(
+    Client = #tclient{
+        protocol = Proto0,
+        seqid = SeqId
+    },
+    Function,
+    ReplyType
+) ->
     case thrift_protocol:read(Proto0, message_begin) of
-         {Proto1, {error, Reason}} ->
-             NewClient = Client#tclient{protocol = Proto1},
-             {NewClient, {error, Reason}};
-         {Proto1, MessageBegin} ->
-             NewClient = Client#tclient{protocol = Proto1},
-             case MessageBegin of
-                 #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId ->
-                     {NewClient, {error, {bad_seq_id, SeqId}}};
-                 #protocol_message_begin{type = ?tMessageType_EXCEPTION} ->
-                     handle_application_exception(NewClient);
-                 #protocol_message_begin{type = ?tMessageType_REPLY} ->
-                     handle_reply(NewClient, Function, ReplyType)
-             end
+        {Proto1, {error, Reason}} ->
+            NewClient = Client#tclient{protocol = Proto1},
+            {NewClient, {error, Reason}};
+        {Proto1, MessageBegin} ->
+            NewClient = Client#tclient{protocol = Proto1},
+            case MessageBegin of
+                #protocol_message_begin{seqid = RetSeqId} when RetSeqId =/= SeqId ->
+                    {NewClient, {error, {bad_seq_id, SeqId}}};
+                #protocol_message_begin{type = ?tMessageType_EXCEPTION} ->
+                    handle_application_exception(NewClient);
+                #protocol_message_begin{type = ?tMessageType_REPLY} ->
+                    handle_reply(NewClient, Function, ReplyType)
+            end
     end.
 
-
-handle_reply(Client = #tclient{protocol = Proto0,
-                               service = Service},
-             Function,
-             ReplyType) ->
+handle_reply(
+    Client = #tclient{
+        protocol = Proto0,
+        service = Service
+    },
+    Function,
+    ReplyType
+) ->
     {struct, ExceptionFields} = Service:function_info(Function, exceptions),
     ReplyStructDef = {struct, [{0, ReplyType}] ++ ExceptionFields},
     {Proto1, {ok, Reply}} = thrift_protocol:read(Proto0, ReplyStructDef),
@@ -139,8 +158,11 @@
     ReplyList = tuple_to_list(Reply),
     true = length(ReplyList) == length(ExceptionFields) + 1,
     ExceptionVals = tl(ReplyList),
-    Thrown = [X || X <- ExceptionVals,
-                   X =/= undefined],
+    Thrown = [
+        X
+     || X <- ExceptionVals,
+        X =/= undefined
+    ],
     case Thrown of
         [] when ReplyType == {struct, []} ->
             {NewClient, {ok, ok}};
@@ -150,12 +172,14 @@
             throw({NewClient, {exception, Exception}})
     end.
 
+-spec handle_application_exception(tclient()) -> no_return().
 handle_application_exception(Client = #tclient{protocol = Proto0}) ->
     {Proto1, {ok, Exception}} =
         thrift_protocol:read(Proto0, ?TApplicationException_Structure),
     {Proto2, ok} = thrift_protocol:read(Proto1, message_end),
     XRecord = list_to_tuple(
-                ['TApplicationException' | tuple_to_list(Exception)]),
+        ['TApplicationException' | tuple_to_list(Exception)]
+    ),
     error_logger:error_msg("X: ~p~n", [XRecord]),
     true = is_record(XRecord, 'TApplicationException'),
     NewClient = Client#tclient{protocol = Proto2},