erlang: Refactor the write paths of the client and protocol

TODO: squash this

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@990980 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/erl/src/thrift_protocol.erl b/lib/erl/src/thrift_protocol.erl
index 3da526c..1caba1b 100644
--- a/lib/erl/src/thrift_protocol.erl
+++ b/lib/erl/src/thrift_protocol.erl
@@ -49,10 +49,11 @@
     {ok, #protocol{module = Module,
                    data = Data}}.
 
--spec flush_transport(#protocol{}) -> ok.
-flush_transport(#protocol{module = Module,
-                          data = Data}) ->
-    Module:flush_transport(Data).
+-spec flush_transport(#protocol{}) -> {#protocol{}, ok}.
+flush_transport(Proto = #protocol{module = Module,
+                                  data = Data}) ->
+    {NewData, Result} = Module:flush_transport(Data),
+    {Proto#protocol{data = NewData}, Result}.
 
 -spec close_transport(#protocol{}) -> ok.
 close_transport(#protocol{module = Module,
@@ -292,16 +293,16 @@
 %%
 %% Description:
 %%--------------------------------------------------------------------
--spec write(#protocol{}, term()) -> ok | {error, _Reason}.
+-spec write(#protocol{}, term()) -> {#protocol{}, ok | {error, _Reason}}.
 
-write(Proto, {{struct, StructDef}, Data})
+write(Proto0, {{struct, StructDef}, Data})
   when is_list(StructDef), is_tuple(Data), length(StructDef) == size(Data) - 1 ->
 
     [StructName | Elems] = tuple_to_list(Data),
-    ok = write(Proto, #protocol_struct_begin{name = StructName}),
-    ok = struct_write_loop(Proto, StructDef, Elems),
-    ok = write(Proto, struct_end),
-    ok;
+    {Proto1, ok} = write(Proto0, #protocol_struct_begin{name = StructName}),
+    {Proto2, ok} = struct_write_loop(Proto1, StructDef, Elems),
+    {Proto3, ok} = write(Proto2, struct_end),
+    {Proto3, ok};
 
 write(Proto, {{struct, {Module, StructureName}}, Data})
   when is_atom(Module),
@@ -310,70 +311,74 @@
     StructType = Module:struct_info(StructureName),
     write(Proto, {Module:struct_info(StructureName), Data});
 
-write(Proto, {{list, Type}, Data})
+write(Proto0, {{list, Type}, Data})
   when is_list(Data) ->
-    ok = write(Proto,
+    {Proto1, ok} = write(Proto0,
                #protocol_list_begin{
                  etype = term_to_typeid(Type),
                  size = length(Data)
                 }),
-    lists:foreach(fun(Elem) ->
-                          ok = write(Proto, {Type, Elem})
-                  end,
-                  Data),
-    ok = write(Proto, list_end),
-    ok;
+    Proto2 = lists:foldl(fun(Elem, ProtoIn) ->
+                                 {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
+                                 ProtoOut
+                         end,
+                         Proto1,
+                         Data),
+    {Proto3, ok} = write(Proto2, list_end),
+    {Proto3, ok};
 
-write(Proto, {{map, KeyType, ValType}, Data}) ->
-    ok = write(Proto,
-               #protocol_map_begin{
-                 ktype = term_to_typeid(KeyType),
-                 vtype = term_to_typeid(ValType),
-                 size  = dict:size(Data)
-                }),
-    dict:fold(fun(KeyData, ValData, _Acc) ->
-                      ok = write(Proto, {KeyType, KeyData}),
-                      ok = write(Proto, {ValType, ValData})
-              end,
-              _AccO = ok,
-              Data),
-    ok = write(Proto, map_end),
-    ok;
+write(Proto0, {{map, KeyType, ValType}, Data}) ->
+    {Proto1, ok} = write(Proto0,
+                         #protocol_map_begin{
+                           ktype = term_to_typeid(KeyType),
+                           vtype = term_to_typeid(ValType),
+                           size  = dict:size(Data)
+                          }),
+    Proto2 = dict:fold(fun(KeyData, ValData, ProtoS0) ->
+                               {ProtoS1, ok} = write(ProtoS0, {KeyType, KeyData}),
+                               {ProtoS2, ok} = write(ProtoS1, {ValType, ValData}),
+                               ProtoS2
+                       end,
+                       Proto1,
+                       Data),
+    {Proto3, ok} = write(Proto2, map_end),
+    {Proto3, ok};
 
-write(Proto, {{set, Type}, Data}) ->
+write(Proto0, {{set, Type}, Data}) ->
     true = sets:is_set(Data),
-    ok = write(Proto,
-               #protocol_set_begin{
-                 etype = term_to_typeid(Type),
-                 size  = sets:size(Data)
-                }),
-    sets:fold(fun(Elem, _Acc) ->
-                      ok = write(Proto, {Type, Elem})
-              end,
-              _Acc0 = ok,
-              Data),
-    ok = write(Proto, set_end),
-    ok;
+    {Proto1, ok} = write(Proto0,
+                         #protocol_set_begin{
+                           etype = term_to_typeid(Type),
+                           size  = sets:size(Data)
+                          }),
+    Proto2 = sets:fold(fun(Elem, ProtoIn) ->
+                               {ProtoOut, ok} = write(ProtoIn, {Type, Elem}),
+                               ProtoOut
+                       end,
+                       Proto1,
+                       Data),
+    {Proto3, ok} = write(Proto2, set_end),
+    {Proto3, ok};
 
-write(#protocol{module = Module,
-                data = ModuleData}, Data) ->
-    Module:write(ModuleData, Data).
+write(Proto = #protocol{module = Module,
+                        data = ModuleData}, Data) ->
+    {NewData, Result} = Module:write(ModuleData, Data),
+    {Proto#protocol{data = NewData}, Result}.
 
-struct_write_loop(Proto, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
-    case Data of
-        undefined ->
-            % null fields are skipped in response
-            skip;
-        _ ->
-            ok = write(Proto,
-                       #protocol_field_begin{
-                         type = term_to_typeid(Type),
-                         id = Fid
-                        }),
-            ok = write(Proto, {Type, Data}),
-            ok = write(Proto, field_end)
-    end,
-    struct_write_loop(Proto, RestStructDef, RestData);
+struct_write_loop(Proto0, [{Fid, Type} | RestStructDef], [Data | RestData]) ->
+    NewProto = case Data of
+                   undefined ->
+                       Proto0; % null fields are skipped in response
+                   _ ->
+                       {Proto1, ok} = write(Proto0,
+                                           #protocol_field_begin{
+                                             type = term_to_typeid(Type),
+                                             id = Fid
+                                            }),
+                       {Proto2, ok} = write(Proto1, {Type, Data}),
+                       {Proto3, ok} = write(Proto2, field_end),
+                       Proto3
+               end,
+    struct_write_loop(NewProto, RestStructDef, RestData);
 struct_write_loop(Proto, [], []) ->
-    ok = write(Proto, field_stop),
-    ok.
+    write(Proto, field_stop).