@@ -819,3 +819,184 @@ TEST_CASE("Test table with a funny value no auto")
819
819
json_str);
820
820
REQUIRE (loaded == value);
821
821
}
822
+
823
+ namespace {
824
+
825
+ struct int_key
826
+ {
827
+ BOOST_HANA_DEFINE_STRUCT (int_key, (int , id));
828
+ };
829
+ DEFINE_OPERATIONS (int_key);
830
+
831
+ struct string_key
832
+ {
833
+ BOOST_HANA_DEFINE_STRUCT (string_key, (std::string, id));
834
+ };
835
+ DEFINE_OPERATIONS (string_key);
836
+
837
+ struct test_champs
838
+ {
839
+ BOOST_HANA_DEFINE_STRUCT (test_champs,
840
+ (immer::map<int , std::string>, map),
841
+ (immer::table<int_key>, table),
842
+ (immer::set<int >, set)
843
+
844
+ );
845
+ };
846
+ DEFINE_OPERATIONS (test_champs);
847
+
848
+ } // namespace
849
+
850
+ TEST_CASE (" Structure breaks when hash is changed" )
851
+ {
852
+ const auto value = test_champs{
853
+ .map = {{123 , " 123" }, {456 , " 456" }},
854
+ };
855
+
856
+ const auto names = immer::archive::get_archives_for_types (
857
+ hana::tuple_t <test_champs>, hana::make_map ());
858
+
859
+ const auto [json_str, ar] =
860
+ immer::archive::to_json_with_auto_archive (value, names);
861
+ // REQUIRE(json_str == "");
862
+
863
+ constexpr auto convert_pair = [](const std::pair<int , std::string>& old) {
864
+ return std::make_pair (fmt::format (" _{}_" , old.first ), old.second );
865
+ };
866
+
867
+ const auto map = hana::make_map (hana::make_pair (
868
+ hana::type_c<immer::map<int , std::string>>,
869
+ hana::overload (convert_pair,
870
+ [](immer::archive::target_container_type_request) {
871
+ // We just return the desired new type, but the hash
872
+ // of int is not compatible with the hash of string.
873
+ return immer::map<std::string, std::string>{};
874
+ }))
875
+
876
+ );
877
+
878
+ auto load_ar = immer::archive::transform_save_archive (ar, map);
879
+
880
+ REQUIRE_THROWS_AS (immer::archive::convert_container (ar, load_ar, value.map ),
881
+ immer::archive::champ::hash_validation_failed_exception);
882
+ }
883
+
884
+ TEST_CASE (" Converting between incompatible keys" )
885
+ {
886
+ const auto value = test_champs{
887
+ .map = {{123 , " 123" }, {456 , " 456" }},
888
+ .table = {{901 }, {902 }},
889
+ };
890
+
891
+ const auto names = immer::archive::get_archives_for_types (
892
+ hana::tuple_t <test_champs>, hana::make_map ());
893
+
894
+ const auto [json_str, ar] =
895
+ immer::archive::to_json_with_auto_archive (value, names);
896
+ // REQUIRE(json_str == "");
897
+
898
+ constexpr auto convert_pair = [](const std::pair<int , std::string>& old) {
899
+ return std::make_pair (fmt::format (" _{}_" , old.first ), old.second );
900
+ };
901
+
902
+ constexpr auto convert_int_key = [](const int_key& old) {
903
+ return string_key{fmt::format (" x{}x" , old.id )};
904
+ };
905
+
906
+ /* *
907
+ * The problem is that the new key of the map has a completely different
908
+ * hash from the old key, which makes the whole map structure unusable. We
909
+ * need to have some special mode that essentially rebuilds the map. We will
910
+ * lose all internal structural sharing but at least the same container_id
911
+ * must return the same container (root node sharing).
912
+ */
913
+ const auto map = hana::make_map (
914
+ hana::make_pair (
915
+ hana::type_c<immer::map<int , std::string>>,
916
+ hana::overload (
917
+ convert_pair,
918
+ [](immer::archive::target_container_type_request) {
919
+ return immer::archive::champ::incompatible_hash_wrapper<
920
+ immer::map<std::string, std::string>>{};
921
+ })),
922
+ hana::make_pair (
923
+ hana::type_c<immer::table<int_key>>,
924
+ hana::overload (
925
+ convert_int_key,
926
+ [](immer::archive::target_container_type_request) {
927
+ return immer::archive::champ::incompatible_hash_wrapper<
928
+ immer::table<string_key>>{};
929
+ })),
930
+ hana::make_pair (
931
+ hana::type_c<immer::set<int >>,
932
+ hana::overload (
933
+ [convert_int_key](int old) {
934
+ return convert_int_key (int_key{old}).id ;
935
+ },
936
+ [](immer::archive::target_container_type_request) {
937
+ return immer::archive::champ::incompatible_hash_wrapper<
938
+ immer::set<std::string>>{};
939
+ }))
940
+
941
+ );
942
+
943
+ auto load_ar = immer::archive::transform_save_archive (ar, map);
944
+ SECTION (" maps" )
945
+ {
946
+ constexpr auto convert_map = [convert_pair](const auto & map) {
947
+ auto result = immer::map<std::string, std::string>{};
948
+ for (const auto & item : map) {
949
+ result = std::move (result).insert (convert_pair (item));
950
+ }
951
+ return result;
952
+ };
953
+
954
+ const auto converted =
955
+ immer::archive::convert_container (ar, load_ar, value.map );
956
+ REQUIRE (converted == convert_map (value.map ));
957
+
958
+ // Converting the same thing should return the same data
959
+ const auto converted_2 =
960
+ immer::archive::convert_container (ar, load_ar, value.map );
961
+ REQUIRE (converted.identity () == converted_2.identity ());
962
+ }
963
+ SECTION (" tables" )
964
+ {
965
+ constexpr auto convert_table = [convert_int_key](const auto & table) {
966
+ auto result = immer::table<string_key>{};
967
+ for (const auto & item : table) {
968
+ result = std::move (result).insert (convert_int_key (item));
969
+ }
970
+ return result;
971
+ };
972
+
973
+ const auto converted =
974
+ immer::archive::convert_container (ar, load_ar, value.table );
975
+ REQUIRE (converted == convert_table (value.table ));
976
+
977
+ // Converting the same thing should return the same data
978
+ const auto converted_2 =
979
+ immer::archive::convert_container (ar, load_ar, value.table );
980
+ REQUIRE (converted.impl ().root == converted_2.impl ().root );
981
+ }
982
+ SECTION (" sets" )
983
+ {
984
+ constexpr auto convert_set = [convert_int_key](const auto & set) {
985
+ auto result = immer::set<std::string>{};
986
+ for (const auto & item : set) {
987
+ result =
988
+ std::move (result).insert (convert_int_key (int_key{item}).id );
989
+ }
990
+ return result;
991
+ };
992
+
993
+ const auto converted =
994
+ immer::archive::convert_container (ar, load_ar, value.set );
995
+ REQUIRE (converted == convert_set (value.set ));
996
+
997
+ // Converting the same thing should return the same data
998
+ const auto converted_2 =
999
+ immer::archive::convert_container (ar, load_ar, value.set );
1000
+ REQUIRE (converted.impl ().root == converted_2.impl ().root );
1001
+ }
1002
+ }
0 commit comments