emqx_map_lib.erl 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2020-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_map_lib).
  17. -export([
  18. deep_get/2,
  19. deep_get/3,
  20. deep_find/2,
  21. deep_put/3,
  22. deep_force_put/3,
  23. deep_remove/2,
  24. deep_merge/2,
  25. safe_atom_key_map/1,
  26. unsafe_atom_key_map/1,
  27. jsonable_map/1,
  28. jsonable_map/2,
  29. binary_string/1,
  30. deep_convert/3,
  31. diff_maps/2,
  32. merge_with/3,
  33. best_effort_recursive_sum/3
  34. ]).
  35. -export_type([config_key/0, config_key_path/0]).
  36. -type config_key() :: atom() | binary() | [byte()].
  37. -type config_key_path() :: [config_key()].
  38. -type convert_fun() :: fun((...) -> {K1 :: any(), V1 :: any()} | drop).
  39. %%-----------------------------------------------------------------
  40. -spec deep_get(config_key_path(), map()) -> term().
  41. deep_get(ConfKeyPath, Map) ->
  42. Ref = make_ref(),
  43. Res = deep_get(ConfKeyPath, Map, Ref),
  44. case Res =:= Ref of
  45. true -> error({config_not_found, ConfKeyPath});
  46. false -> Res
  47. end.
  48. -spec deep_get(config_key_path(), map(), term()) -> term().
  49. deep_get(ConfKeyPath, Map, Default) ->
  50. case deep_find(ConfKeyPath, Map) of
  51. {not_found, _KeyPath, _Data} -> Default;
  52. {ok, Data} -> Data
  53. end.
  54. -spec deep_find(config_key_path(), map()) ->
  55. {ok, term()} | {not_found, config_key_path(), term()}.
  56. deep_find([], Map) ->
  57. {ok, Map};
  58. deep_find([Key | KeyPath] = Path, Map) when is_map(Map) ->
  59. case maps:find(Key, Map) of
  60. {ok, SubMap} -> deep_find(KeyPath, SubMap);
  61. error -> {not_found, Path, Map}
  62. end;
  63. deep_find(KeyPath, Data) ->
  64. {not_found, KeyPath, Data}.
  65. -spec deep_put(config_key_path(), map(), term()) -> map().
  66. deep_put([], _Map, Data) ->
  67. Data;
  68. deep_put([Key | KeyPath], Map, Data) ->
  69. SubMap = maps:get(Key, Map, #{}),
  70. Map#{Key => deep_put(KeyPath, SubMap, Data)}.
  71. %% Like deep_put, but ensures that the key path is present.
  72. %% If key path is not present in map, creates the keys, until it's present
  73. %% deep_force_put([x, y, z], #{a => 1}, 0) -> #{a => 1, x => #{y => #{z => 0}}}
  74. -spec deep_force_put(config_key_path(), map(), term()) -> map().
  75. deep_force_put([], _Map, Data) ->
  76. Data;
  77. deep_force_put([Key | KeyPath] = FullPath, Map, Data) ->
  78. case Map of
  79. #{Key := InnerValue} ->
  80. Map#{Key => deep_force_put(KeyPath, InnerValue, Data)};
  81. #{} ->
  82. maps:put(Key, path_to_map(KeyPath, Data), Map);
  83. _ ->
  84. path_to_map(FullPath, Data)
  85. end.
  86. -spec path_to_map(config_key_path(), term()) -> map().
  87. path_to_map([], Data) -> Data;
  88. path_to_map([Key | Tail], Data) -> #{Key => path_to_map(Tail, Data)}.
  89. -spec deep_remove(config_key_path(), map()) -> map().
  90. deep_remove([], Map) ->
  91. Map;
  92. deep_remove([Key], Map) ->
  93. maps:remove(Key, Map);
  94. deep_remove([Key | KeyPath], Map) ->
  95. case maps:find(Key, Map) of
  96. {ok, SubMap} when is_map(SubMap) ->
  97. Map#{Key => deep_remove(KeyPath, SubMap)};
  98. {ok, _Val} ->
  99. Map;
  100. error ->
  101. Map
  102. end.
  103. %% #{a => #{b => 3, c => 2}, d => 4}
  104. %% = deep_merge(#{a => #{b => 1, c => 2}, d => 4}, #{a => #{b => 3}}).
  105. -spec deep_merge(map(), map()) -> map().
  106. deep_merge(BaseMap, NewMap) ->
  107. NewKeys = maps:keys(NewMap) -- maps:keys(BaseMap),
  108. MergedBase = maps:fold(
  109. fun(K, V, Acc) ->
  110. case maps:find(K, NewMap) of
  111. error ->
  112. Acc#{K => V};
  113. {ok, NewV} when is_map(V), is_map(NewV) ->
  114. Acc#{K => deep_merge(V, NewV)};
  115. {ok, NewV} ->
  116. Acc#{K => NewV}
  117. end
  118. end,
  119. #{},
  120. BaseMap
  121. ),
  122. maps:merge(MergedBase, maps:with(NewKeys, NewMap)).
  123. -spec deep_convert(map(), convert_fun(), Args :: list()) -> map().
  124. deep_convert(Map, ConvFun, Args) when is_map(Map) ->
  125. maps:fold(
  126. fun(K, V, Acc) ->
  127. case apply(ConvFun, [K, deep_convert(V, ConvFun, Args) | Args]) of
  128. drop -> Acc;
  129. {K1, V1} -> Acc#{K1 => V1}
  130. end
  131. end,
  132. #{},
  133. Map
  134. );
  135. deep_convert(ListV, ConvFun, Args) when is_list(ListV) ->
  136. [deep_convert(V, ConvFun, Args) || V <- ListV];
  137. deep_convert(Val, _, _Args) ->
  138. Val.
  139. -spec unsafe_atom_key_map(#{binary() | atom() => any()}) -> #{atom() => any()}.
  140. unsafe_atom_key_map(Map) ->
  141. covert_keys_to_atom(Map, fun(K) -> binary_to_atom(K, utf8) end).
  142. -spec safe_atom_key_map(#{binary() | atom() => any()}) -> #{atom() => any()}.
  143. safe_atom_key_map(Map) ->
  144. covert_keys_to_atom(Map, fun(K) -> binary_to_existing_atom(K, utf8) end).
  145. -spec jsonable_map(map() | list()) -> map() | list().
  146. jsonable_map(Map) ->
  147. jsonable_map(Map, fun(K, V) -> {K, V} end).
  148. jsonable_map(Map, JsonableFun) ->
  149. deep_convert(Map, fun binary_string_kv/3, [JsonableFun]).
  150. -spec diff_maps(map(), map()) ->
  151. #{
  152. added := map(),
  153. identical := map(),
  154. removed := map(),
  155. changed := #{any() => {OldValue :: any(), NewValue :: any()}}
  156. }.
  157. diff_maps(NewMap, OldMap) ->
  158. InitR = #{identical => #{}, changed => #{}, removed => #{}},
  159. {Result, RemInNew} =
  160. lists:foldl(
  161. fun({OldK, OldV}, {Result0 = #{identical := I, changed := U, removed := D}, RemNewMap}) ->
  162. Result1 =
  163. case maps:find(OldK, NewMap) of
  164. error ->
  165. Result0#{removed => D#{OldK => OldV}};
  166. {ok, NewV} when NewV == OldV ->
  167. Result0#{identical => I#{OldK => OldV}};
  168. {ok, NewV} ->
  169. Result0#{changed => U#{OldK => {OldV, NewV}}}
  170. end,
  171. {Result1, maps:remove(OldK, RemNewMap)}
  172. end,
  173. {InitR, NewMap},
  174. maps:to_list(OldMap)
  175. ),
  176. Result#{added => RemInNew}.
  177. binary_string_kv(K, V, JsonableFun) ->
  178. case JsonableFun(K, V) of
  179. drop -> drop;
  180. {K1, V1} -> {binary_string(K1), V1}
  181. end.
  182. binary_string([]) ->
  183. [];
  184. binary_string(Val) when is_list(Val) ->
  185. case io_lib:printable_unicode_list(Val) of
  186. true -> unicode:characters_to_binary(Val);
  187. false -> [binary_string(V) || V <- Val]
  188. end;
  189. binary_string(Val) ->
  190. Val.
  191. %%---------------------------------------------------------------------------
  192. covert_keys_to_atom(BinKeyMap, Conv) ->
  193. deep_convert(
  194. BinKeyMap,
  195. fun
  196. (K, V) when is_atom(K) -> {K, V};
  197. (K, V) when is_binary(K) -> {Conv(K), V}
  198. end,
  199. []
  200. ).
  201. %% copy from maps.erl OTP24.0
  202. merge_with(Combiner, Map1, Map2) when
  203. is_map(Map1),
  204. is_map(Map2),
  205. is_function(Combiner, 3)
  206. ->
  207. case map_size(Map1) > map_size(Map2) of
  208. true ->
  209. Iterator = maps:iterator(Map2),
  210. merge_with_t(
  211. maps:next(Iterator),
  212. Map1,
  213. Map2,
  214. Combiner
  215. );
  216. false ->
  217. Iterator = maps:iterator(Map1),
  218. merge_with_t(
  219. maps:next(Iterator),
  220. Map2,
  221. Map1,
  222. fun(K, V1, V2) -> Combiner(K, V2, V1) end
  223. )
  224. end;
  225. merge_with(Combiner, Map1, Map2) ->
  226. ErrorType = error_type_merge_intersect(Map1, Map2, Combiner),
  227. throw(#{maps_merge_error => ErrorType, args => [Map1, Map2]}).
  228. merge_with_t({K, V2, Iterator}, Map1, Map2, Combiner) ->
  229. case Map1 of
  230. #{K := V1} ->
  231. NewMap1 = Map1#{K := Combiner(K, V1, V2)},
  232. merge_with_t(maps:next(Iterator), NewMap1, Map2, Combiner);
  233. #{} ->
  234. merge_with_t(maps:next(Iterator), maps:put(K, V2, Map1), Map2, Combiner)
  235. end;
  236. merge_with_t(none, Result, _, _) ->
  237. Result.
  238. error_type_merge_intersect(M1, M2, Combiner) when is_function(Combiner, 3) ->
  239. error_type_two_maps(M1, M2);
  240. error_type_merge_intersect(_M1, _M2, _Combiner) ->
  241. badarg_combiner_function.
  242. error_type_two_maps(M1, M2) when is_map(M1) ->
  243. {badmap, M2};
  244. error_type_two_maps(M1, _M2) ->
  245. {badmap, M1}.
  246. %% @doc Sum-merge map values.
  247. %% For bad merges, ErrorLogger is called to log the key, and value in M2 is ignored.
  248. best_effort_recursive_sum(M10, M20, ErrorLogger) ->
  249. FilterF = fun(K, V) ->
  250. case erlang:is_number(V) of
  251. true ->
  252. true;
  253. false ->
  254. ErrorLogger(#{failed_to_merge => K, bad_value => V}),
  255. false
  256. end
  257. end,
  258. M1 = deep_filter(M10, FilterF),
  259. M2 = deep_filter(M20, FilterF),
  260. do_best_effort_recursive_sum(M1, M2, ErrorLogger).
  261. do_best_effort_recursive_sum(M1, M2, ErrorLogger) ->
  262. F =
  263. fun(Key, V1, V2) ->
  264. case {erlang:is_map(V1), erlang:is_map(V2)} of
  265. {true, true} ->
  266. do_best_effort_recursive_sum(V1, V2, ErrorLogger);
  267. {true, false} ->
  268. ErrorLogger(#{failed_to_merge => Key, bad_value => V2}),
  269. do_best_effort_recursive_sum(V1, #{}, ErrorLogger);
  270. {false, true} ->
  271. ErrorLogger(#{failed_to_merge => Key, bad_value => V1}),
  272. do_best_effort_recursive_sum(V2, #{}, ErrorLogger);
  273. {false, false} ->
  274. true = is_number(V1),
  275. true = is_number(V2),
  276. V1 + V2
  277. end
  278. end,
  279. merge_with(F, M1, M2).
  280. deep_filter(M, F) when is_map(M) ->
  281. %% maps:filtermap is not available before OTP 24
  282. maps:from_list(
  283. lists:filtermap(
  284. fun
  285. ({K, V}) when is_map(V) ->
  286. {true, {K, deep_filter(V, F)}};
  287. ({K, V}) ->
  288. F(K, V) andalso {true, {K, V}}
  289. end,
  290. maps:to_list(M)
  291. )
  292. ).