emqx_map_lib.erl 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. binary_key_map/1,
  26. safe_atom_key_map/1,
  27. unsafe_atom_key_map/1,
  28. jsonable_map/1,
  29. jsonable_map/2,
  30. binary_string/1,
  31. deep_convert/3,
  32. diff_maps/2,
  33. merge_with/3,
  34. best_effort_recursive_sum/3,
  35. if_only_to_toggle_enable/2
  36. ]).
  37. -export_type([config_key/0, config_key_path/0]).
  38. -type config_key() :: atom() | binary() | [byte()].
  39. -type config_key_path() :: [config_key()].
  40. -type convert_fun() :: fun((...) -> {K1 :: any(), V1 :: any()} | drop).
  41. %%-----------------------------------------------------------------
  42. -spec deep_get(config_key_path(), map()) -> term().
  43. deep_get(ConfKeyPath, Map) ->
  44. Ref = make_ref(),
  45. Res = deep_get(ConfKeyPath, Map, Ref),
  46. case Res =:= Ref of
  47. true -> error({config_not_found, ConfKeyPath});
  48. false -> Res
  49. end.
  50. -spec deep_get(config_key_path(), map(), term()) -> term().
  51. deep_get(ConfKeyPath, Map, Default) ->
  52. case deep_find(ConfKeyPath, Map) of
  53. {not_found, _KeyPath, _Data} -> Default;
  54. {ok, Data} -> Data
  55. end.
  56. -spec deep_find(config_key_path(), map()) ->
  57. {ok, term()} | {not_found, config_key_path(), term()}.
  58. deep_find([], Map) ->
  59. {ok, Map};
  60. deep_find([Key | KeyPath] = Path, Map) when is_map(Map) ->
  61. case maps:find(Key, Map) of
  62. {ok, SubMap} -> deep_find(KeyPath, SubMap);
  63. error -> {not_found, Path, Map}
  64. end;
  65. deep_find(KeyPath, Data) ->
  66. {not_found, KeyPath, Data}.
  67. -spec deep_put(config_key_path(), map(), term()) -> map().
  68. deep_put([], _Map, Data) ->
  69. Data;
  70. deep_put([Key | KeyPath], Map, Data) ->
  71. SubMap = maps:get(Key, Map, #{}),
  72. Map#{Key => deep_put(KeyPath, SubMap, Data)}.
  73. %% Like deep_put, but ensures that the key path is present.
  74. %% If key path is not present in map, creates the keys, until it's present
  75. %% deep_force_put([x, y, z], #{a => 1}, 0) -> #{a => 1, x => #{y => #{z => 0}}}
  76. -spec deep_force_put(config_key_path(), map(), term()) -> map().
  77. deep_force_put([], _Map, Data) ->
  78. Data;
  79. deep_force_put([Key | KeyPath] = FullPath, Map, Data) ->
  80. case Map of
  81. #{Key := InnerValue} ->
  82. Map#{Key => deep_force_put(KeyPath, InnerValue, Data)};
  83. #{} ->
  84. maps:put(Key, path_to_map(KeyPath, Data), Map);
  85. _ ->
  86. path_to_map(FullPath, Data)
  87. end.
  88. -spec path_to_map(config_key_path(), term()) -> map().
  89. path_to_map([], Data) -> Data;
  90. path_to_map([Key | Tail], Data) -> #{Key => path_to_map(Tail, Data)}.
  91. -spec deep_remove(config_key_path(), map()) -> map().
  92. deep_remove([], Map) ->
  93. Map;
  94. deep_remove([Key], Map) ->
  95. maps:remove(Key, Map);
  96. deep_remove([Key | KeyPath], Map) ->
  97. case maps:find(Key, Map) of
  98. {ok, SubMap} when is_map(SubMap) ->
  99. Map#{Key => deep_remove(KeyPath, SubMap)};
  100. {ok, _Val} ->
  101. Map;
  102. error ->
  103. Map
  104. end.
  105. %% #{a => #{b => 3, c => 2}, d => 4}
  106. %% = deep_merge(#{a => #{b => 1, c => 2}, d => 4}, #{a => #{b => 3}}).
  107. -spec deep_merge(map(), map()) -> map().
  108. deep_merge(BaseMap, NewMap) ->
  109. NewKeys = maps:keys(NewMap) -- maps:keys(BaseMap),
  110. MergedBase = maps:fold(
  111. fun(K, V, Acc) ->
  112. case maps:find(K, NewMap) of
  113. error ->
  114. Acc#{K => V};
  115. {ok, NewV} when is_map(V), is_map(NewV) ->
  116. Acc#{K => deep_merge(V, NewV)};
  117. {ok, NewV} ->
  118. Acc#{K => NewV}
  119. end
  120. end,
  121. #{},
  122. BaseMap
  123. ),
  124. maps:merge(MergedBase, maps:with(NewKeys, NewMap)).
  125. -spec deep_convert(any(), convert_fun(), Args :: list()) -> any().
  126. deep_convert(Map, ConvFun, Args) when is_map(Map) ->
  127. maps:fold(
  128. fun(K, V, Acc) ->
  129. case apply(ConvFun, [K, deep_convert(V, ConvFun, Args) | Args]) of
  130. drop -> Acc;
  131. {K1, V1} -> Acc#{K1 => V1}
  132. end
  133. end,
  134. #{},
  135. Map
  136. );
  137. deep_convert(ListV, ConvFun, Args) when is_list(ListV) ->
  138. [deep_convert(V, ConvFun, Args) || V <- ListV];
  139. deep_convert(Val, _, _Args) ->
  140. Val.
  141. -spec unsafe_atom_key_map(#{binary() | atom() => any()}) -> #{atom() => any()}.
  142. unsafe_atom_key_map(Map) ->
  143. covert_keys_to_atom(Map, fun(K) -> binary_to_atom(K, utf8) end).
  144. -spec binary_key_map(map()) -> map().
  145. binary_key_map(Map) ->
  146. deep_convert(
  147. Map,
  148. fun
  149. (K, V) when is_atom(K) -> {atom_to_binary(K, utf8), V};
  150. (K, V) when is_binary(K) -> {K, V}
  151. end,
  152. []
  153. ).
  154. -spec safe_atom_key_map(#{binary() | atom() => any()}) -> #{atom() => any()}.
  155. safe_atom_key_map(Map) ->
  156. covert_keys_to_atom(Map, fun(K) -> binary_to_existing_atom(K, utf8) end).
  157. -spec jsonable_map(map() | list()) -> map() | list().
  158. jsonable_map(Map) ->
  159. jsonable_map(Map, fun(K, V) -> {K, V} end).
  160. jsonable_map(Map, JsonableFun) ->
  161. deep_convert(Map, fun binary_string_kv/3, [JsonableFun]).
  162. -spec diff_maps(map(), map()) ->
  163. #{
  164. added := map(),
  165. identical := map(),
  166. removed := map(),
  167. changed := #{any() => {OldValue :: any(), NewValue :: any()}}
  168. }.
  169. diff_maps(NewMap, OldMap) ->
  170. InitR = #{identical => #{}, changed => #{}, removed => #{}},
  171. {Result, RemInNew} =
  172. lists:foldl(
  173. fun({OldK, OldV}, {Result0 = #{identical := I, changed := U, removed := D}, RemNewMap}) ->
  174. Result1 =
  175. case maps:find(OldK, NewMap) of
  176. error ->
  177. Result0#{removed => D#{OldK => OldV}};
  178. {ok, NewV} when NewV == OldV ->
  179. Result0#{identical => I#{OldK => OldV}};
  180. {ok, NewV} ->
  181. Result0#{changed => U#{OldK => {OldV, NewV}}}
  182. end,
  183. {Result1, maps:remove(OldK, RemNewMap)}
  184. end,
  185. {InitR, NewMap},
  186. maps:to_list(OldMap)
  187. ),
  188. Result#{added => RemInNew}.
  189. binary_string_kv(K, V, JsonableFun) ->
  190. case JsonableFun(K, V) of
  191. drop -> drop;
  192. {K1, V1} -> {binary_string(K1), V1}
  193. end.
  194. binary_string([]) ->
  195. [];
  196. binary_string(Val) when is_list(Val) ->
  197. case io_lib:printable_unicode_list(Val) of
  198. true -> unicode:characters_to_binary(Val);
  199. false -> [binary_string(V) || V <- Val]
  200. end;
  201. binary_string(Val) ->
  202. Val.
  203. %%---------------------------------------------------------------------------
  204. covert_keys_to_atom(BinKeyMap, Conv) ->
  205. deep_convert(
  206. BinKeyMap,
  207. fun
  208. (K, V) when is_atom(K) -> {K, V};
  209. (K, V) when is_binary(K) -> {Conv(K), V}
  210. end,
  211. []
  212. ).
  213. %% copy from maps.erl OTP24.0
  214. merge_with(Combiner, Map1, Map2) when
  215. is_map(Map1),
  216. is_map(Map2),
  217. is_function(Combiner, 3)
  218. ->
  219. case map_size(Map1) > map_size(Map2) of
  220. true ->
  221. Iterator = maps:iterator(Map2),
  222. merge_with_t(
  223. maps:next(Iterator),
  224. Map1,
  225. Map2,
  226. Combiner
  227. );
  228. false ->
  229. Iterator = maps:iterator(Map1),
  230. merge_with_t(
  231. maps:next(Iterator),
  232. Map2,
  233. Map1,
  234. fun(K, V1, V2) -> Combiner(K, V2, V1) end
  235. )
  236. end;
  237. merge_with(Combiner, Map1, Map2) ->
  238. ErrorType = error_type_merge_intersect(Map1, Map2, Combiner),
  239. throw(#{maps_merge_error => ErrorType, args => [Map1, Map2]}).
  240. merge_with_t({K, V2, Iterator}, Map1, Map2, Combiner) ->
  241. case Map1 of
  242. #{K := V1} ->
  243. NewMap1 = Map1#{K := Combiner(K, V1, V2)},
  244. merge_with_t(maps:next(Iterator), NewMap1, Map2, Combiner);
  245. #{} ->
  246. merge_with_t(maps:next(Iterator), maps:put(K, V2, Map1), Map2, Combiner)
  247. end;
  248. merge_with_t(none, Result, _, _) ->
  249. Result.
  250. error_type_merge_intersect(M1, M2, Combiner) when is_function(Combiner, 3) ->
  251. error_type_two_maps(M1, M2);
  252. error_type_merge_intersect(_M1, _M2, _Combiner) ->
  253. badarg_combiner_function.
  254. error_type_two_maps(M1, M2) when is_map(M1) ->
  255. {badmap, M2};
  256. error_type_two_maps(M1, _M2) ->
  257. {badmap, M1}.
  258. %% @doc Sum-merge map values.
  259. %% For bad merges, ErrorLogger is called to log the key, and value in M2 is ignored.
  260. best_effort_recursive_sum(M10, M20, ErrorLogger) ->
  261. FilterF = fun(K, V) ->
  262. case erlang:is_number(V) of
  263. true ->
  264. true;
  265. false ->
  266. ErrorLogger(#{failed_to_merge => K, bad_value => V}),
  267. false
  268. end
  269. end,
  270. M1 = deep_filter(M10, FilterF),
  271. M2 = deep_filter(M20, FilterF),
  272. do_best_effort_recursive_sum(M1, M2, ErrorLogger).
  273. do_best_effort_recursive_sum(M1, M2, ErrorLogger) ->
  274. F =
  275. fun(Key, V1, V2) ->
  276. case {erlang:is_map(V1), erlang:is_map(V2)} of
  277. {true, true} ->
  278. do_best_effort_recursive_sum(V1, V2, ErrorLogger);
  279. {true, false} ->
  280. ErrorLogger(#{failed_to_merge => Key, bad_value => V2}),
  281. do_best_effort_recursive_sum(V1, #{}, ErrorLogger);
  282. {false, true} ->
  283. ErrorLogger(#{failed_to_merge => Key, bad_value => V1}),
  284. do_best_effort_recursive_sum(V2, #{}, ErrorLogger);
  285. {false, false} ->
  286. true = is_number(V1),
  287. true = is_number(V2),
  288. V1 + V2
  289. end
  290. end,
  291. merge_with(F, M1, M2).
  292. deep_filter(M, F) when is_map(M) ->
  293. %% maps:filtermap is not available before OTP 24
  294. maps:from_list(
  295. lists:filtermap(
  296. fun
  297. ({K, V}) when is_map(V) ->
  298. {true, {K, deep_filter(V, F)}};
  299. ({K, V}) ->
  300. F(K, V) andalso {true, {K, V}}
  301. end,
  302. maps:to_list(M)
  303. )
  304. ).
  305. if_only_to_toggle_enable(OldConf, Conf) ->
  306. #{added := Added, removed := Removed, changed := Updated} =
  307. emqx_map_lib:diff_maps(OldConf, Conf),
  308. case {Added, Removed, Updated} of
  309. {Added, Removed, #{enable := _} = Updated} when
  310. map_size(Added) =:= 0,
  311. map_size(Removed) =:= 0,
  312. map_size(Updated) =:= 1
  313. ->
  314. true;
  315. {_, _, _} ->
  316. false
  317. end.