THRIFT-3214 Add Erlang option for using maps instead of dicts
Client: Erlang
Patch: Michael Oliver <mikemboliver@gmail.com>

This closes #535
diff --git a/compiler/cpp/src/generate/t_erl_generator.cc b/compiler/cpp/src/generate/t_erl_generator.cc
index 342c581..8af5da2 100644
--- a/compiler/cpp/src/generate/t_erl_generator.cc
+++ b/compiler/cpp/src/generate/t_erl_generator.cc
@@ -55,6 +55,7 @@
     out_dir_base_ = "gen-erl";
 
     legacy_names_ = (parsed_options.find("legacynames") != parsed_options.end());
+    maps_ = (parsed_options.find("maps") != parsed_options.end());
   }
 
   /**
@@ -152,6 +153,9 @@
   /* if true retain pre 0.9.2 naming scheme for functions, atoms and consts */
   bool legacy_names_;
 
+  /* if true use maps instead of dicts in generated code */
+  bool maps_;
+
   /**
    * add function to export list
    */
@@ -430,7 +434,11 @@
     t_type* ktype = ((t_map*)type)->get_key_type();
     t_type* vtype = ((t_map*)type)->get_val_type();
 
-    out << "dict:from_list([";
+    if (maps_) {
+      out << "maps:from_list([";
+    } else {
+      out << "dict:from_list([";
+    }
     map<t_const_value*, t_const_value*>::const_iterator i, end = value->get_map().end();
     for (i = value->get_map().begin(); i != end;) {
       out << "{" << render_const_value(ktype, i->first) << ","
@@ -479,7 +487,11 @@
   if (type->is_struct() || type->is_xception()) {
     return "#" + atomify(type->get_name()) + "{}";
   } else if (type->is_map()) {
-    return "dict:new()";
+    if (maps_) {
+      return "#{}";
+    } else {
+      return "dict:new()";
+    }
   } else if (type->is_set()) {
     return "sets:new()";
   } else if (type->is_list()) {
@@ -513,7 +525,11 @@
   } else if (type->is_struct() || type->is_xception()) {
     return atomify(type->get_name()) + "()";
   } else if (type->is_map()) {
-    return "dict:dict()";
+    if (maps_) {
+      return "#{}";
+    } else {
+      return "dict:dict()";
+    }
   } else if (type->is_set()) {
     return "sets:set()";
   } else if (type->is_list()) {
@@ -1010,4 +1026,5 @@
 THRIFT_REGISTER_GENERATOR(
     erl,
     "Erlang",
-    "    legacynames: Output files retain naming conventions of Thrift 0.9.1 and earlier.\n")
+    "    legacynames: Output files retain naming conventions of Thrift 0.9.1 and earlier.\n"
+    "    maps:        Generate maps instead of dicts.\n")
diff --git a/lib/erl/Makefile.am b/lib/erl/Makefile.am
index 60c7e5a..1f65a24 100644
--- a/lib/erl/Makefile.am
+++ b/lib/erl/Makefile.am
@@ -25,6 +25,7 @@
 	for f in $(THRIFT_FILES) ; do \
 	  $(THRIFT) --gen erl -o test $$f ; \
 	done ; \
+	$(THRIFT) --gen erl:maps -o test test/Thrift3214.thrift ; \
 	touch .generated
 
 all: .generated
diff --git a/lib/erl/test/Thrift3214.thrift b/lib/erl/test/Thrift3214.thrift
new file mode 100644
index 0000000..a9110ce
--- /dev/null
+++ b/lib/erl/test/Thrift3214.thrift
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+struct StringMap
+{
+  1: map<i32, string> data = {1: "a", 2: "b"};
+}
diff --git a/lib/erl/test/test_thrift_3214.erl b/lib/erl/test/test_thrift_3214.erl
new file mode 100644
index 0000000..118e779
--- /dev/null
+++ b/lib/erl/test/test_thrift_3214.erl
@@ -0,0 +1,58 @@
+%%
+%% Licensed to the Apache Software Foundation (ASF) under one
+%% or more contributor license agreements. See the NOTICE file
+%% distributed with this work for additional information
+%% regarding copyright ownership. The ASF licenses this file
+%% to you under the Apache License, Version 2.0 (the
+%% "License"); you may not use this file except in compliance
+%% with the License. You may obtain a copy of the License at
+%%
+%%   http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing,
+%% software distributed under the License is distributed on an
+%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+%% KIND, either express or implied. See the License for the
+%% specific language governing permissions and limitations
+%% under the License.
+%%
+
+-module(test_thrift_3214).
+-compile(export_all).
+
+-include("gen-erl/thrift3214_types.hrl").
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+record_generation_test_() ->
+  [
+    {"StringMap record", ?_assertMatch(
+      {'StringMap', _},
+      #'StringMap'{data=#{50 => "foo"}}
+    )},
+    {"StringMap record defaults", ?_assertEqual(
+      {'StringMap', #{1 => "a", 2 => "b"}},
+      #'StringMap'{}
+    )},
+    {"StringMap record dict from list", ?_assertNotEqual(
+      {'StringMap', dict:from_list([{1, "a"}, {2, "b"}])},
+      #'StringMap'{}
+    )},
+    {"StringMap record map from list", ?_assertEqual(
+      {'StringMap', maps:from_list([{1, "a"}, {2, "b"}])},
+      #'StringMap'{}
+    )}
+  ].
+
+struct_info_test_() ->
+  [
+    {"StringMap extended definition", ?_assertEqual(
+      {struct, [
+        {1, undefined, {map, i32, string}, 'data', #{1 => "a", 2 => "b"}}
+      ]},
+      thrift3214_types:struct_info_ext('StringMap')
+    )}
+  ].
+
+-endif.