• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

emqx / emqx / 8628139215

10 Apr 2024 08:18AM UTC coverage: 62.44% (-0.05%) from 62.489%
8628139215

push

github

web-flow
Merge pull request #12851 from zmstone/0327-feat-add-emqx_variform

emqx_variform for string substitution and transform

206 of 238 new or added lines in 3 files covered. (86.55%)

28 existing lines in 16 files now uncovered.

34895 of 55886 relevant lines covered (62.44%)

6585.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.14
/apps/emqx_utils/src/emqx_variform_str.erl
1
%%--------------------------------------------------------------------
2
%% Copyright (c) 2020-2024 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

17
%% Predefined functions string templating
18
-module(emqx_variform_str).
19

20
%% String Funcs
21
-export([
22
    coalesce/1,
23
    coalesce/2,
24
    lower/1,
25
    ltrim/1,
26
    ltrim/2,
27
    reverse/1,
28
    rtrim/1,
29
    rtrim/2,
30
    strlen/1,
31
    substr/2,
32
    substr/3,
33
    trim/1,
34
    trim/2,
35
    upper/1,
36
    split/2,
37
    split/3,
38
    concat/1,
39
    concat/2,
40
    tokens/2,
41
    tokens/3,
42
    sprintf_s/2,
43
    pad/2,
44
    pad/3,
45
    pad/4,
46
    replace/3,
47
    replace/4,
48
    regex_match/2,
49
    regex_replace/3,
50
    ascii/1,
51
    find/2,
52
    find/3,
53
    join_to_string/1,
54
    join_to_string/2,
55
    unescape/1,
56
    nth/2
57
]).
58

59
-define(IS_EMPTY(X), (X =:= <<>> orelse X =:= "" orelse X =:= undefined)).
60

61
%%------------------------------------------------------------------------------
62
%% String Funcs
63
%%------------------------------------------------------------------------------
64

65
%% @doc Return the first non-empty string
66
coalesce(A, B) when ?IS_EMPTY(A) andalso ?IS_EMPTY(B) ->
67
    <<>>;
2✔
68
coalesce(A, B) when ?IS_EMPTY(A) ->
69
    B;
2✔
70
coalesce(A, _B) ->
71
    A.
6✔
72

73
%% @doc Return the first non-empty string
74
coalesce([]) ->
75
    <<>>;
5✔
76
coalesce([H | T]) ->
77
    coalesce(H, coalesce(T)).
10✔
78

79
lower(S) when is_binary(S) ->
80
    string:lowercase(S).
1✔
81

82
ltrim(S) when is_binary(S) ->
83
    string:trim(S, leading).
2✔
84

85
ltrim(S, Chars) ->
NEW
86
    string:trim(S, leading, Chars).
×
87

88
reverse(S) when is_binary(S) ->
89
    iolist_to_binary(string:reverse(S)).
2✔
90

91
rtrim(S) when is_binary(S) ->
92
    string:trim(S, trailing).
2✔
93

94
rtrim(S, Chars) when is_binary(S) ->
NEW
95
    string:trim(S, trailing, Chars).
×
96

97
strlen(S) when is_binary(S) ->
98
    string:length(S).
2✔
99

100
substr(S, Start) when is_binary(S), is_integer(Start) ->
101
    string:slice(S, Start).
2✔
102

103
substr(S, Start, Length) when
104
    is_binary(S),
105
    is_integer(Start),
106
    is_integer(Length)
107
->
108
    string:slice(S, Start, Length).
1✔
109

110
trim(S) when is_binary(S) ->
111
    string:trim(S).
2✔
112

113
trim(S, Chars) when is_binary(S) ->
NEW
114
    string:trim(S, both, Chars).
×
115

116
upper(S) when is_binary(S) ->
117
    string:uppercase(S).
1✔
118

119
split(S, P) when is_binary(S), is_binary(P) ->
120
    [R || R <- string:split(S, P, all), R =/= <<>> andalso R =/= ""].
8✔
121

122
split(S, P, <<"notrim">>) ->
123
    string:split(S, P, all);
7✔
124
split(S, P, <<"leading_notrim">>) ->
125
    string:split(S, P, leading);
7✔
126
split(S, P, <<"leading">>) when is_binary(S), is_binary(P) ->
127
    [R || R <- string:split(S, P, leading), R =/= <<>> andalso R =/= ""];
7✔
128
split(S, P, <<"trailing_notrim">>) ->
129
    string:split(S, P, trailing);
7✔
130
split(S, P, <<"trailing">>) when is_binary(S), is_binary(P) ->
131
    [R || R <- string:split(S, P, trailing), R =/= <<>> andalso R =/= ""].
7✔
132

133
tokens(S, Separators) ->
134
    [list_to_binary(R) || R <- string:lexemes(binary_to_list(S), binary_to_list(Separators))].
10✔
135

136
tokens(S, Separators, <<"nocrlf">>) ->
137
    [
6✔
138
        list_to_binary(R)
12✔
139
     || R <- string:lexemes(binary_to_list(S), binary_to_list(Separators) ++ [$\r, $\n, [$\r, $\n]])
6✔
140
    ].
141

142
%% implicit convert args to strings, and then do concatenation
143
concat(S1, S2) ->
144
    concat([S1, S2]).
8✔
145

146
concat(List) ->
147
    unicode:characters_to_binary(lists:map(fun str/1, List), unicode).
10✔
148

149
sprintf_s(Format, Args) when is_list(Args) ->
150
    erlang:iolist_to_binary(io_lib:format(binary_to_list(Format), Args)).
3✔
151

152
pad(S, Len) when is_binary(S), is_integer(Len) ->
153
    iolist_to_binary(string:pad(S, Len, trailing)).
2✔
154

155
pad(S, Len, <<"trailing">>) when is_binary(S), is_integer(Len) ->
156
    iolist_to_binary(string:pad(S, Len, trailing));
2✔
157
pad(S, Len, <<"both">>) when is_binary(S), is_integer(Len) ->
158
    iolist_to_binary(string:pad(S, Len, both));
2✔
159
pad(S, Len, <<"leading">>) when is_binary(S), is_integer(Len) ->
160
    iolist_to_binary(string:pad(S, Len, leading)).
2✔
161

162
pad(S, Len, <<"trailing">>, Char) when is_binary(S), is_integer(Len), is_binary(Char) ->
163
    Chars = unicode:characters_to_list(Char, utf8),
2✔
164
    iolist_to_binary(string:pad(S, Len, trailing, Chars));
2✔
165
pad(S, Len, <<"both">>, Char) when is_binary(S), is_integer(Len), is_binary(Char) ->
166
    Chars = unicode:characters_to_list(Char, utf8),
2✔
167
    iolist_to_binary(string:pad(S, Len, both, Chars));
2✔
168
pad(S, Len, <<"leading">>, Char) when is_binary(S), is_integer(Len), is_binary(Char) ->
169
    Chars = unicode:characters_to_list(Char, utf8),
2✔
170
    iolist_to_binary(string:pad(S, Len, leading, Chars)).
2✔
171

172
replace(SrcStr, P, RepStr) when is_binary(SrcStr), is_binary(P), is_binary(RepStr) ->
173
    iolist_to_binary(string:replace(SrcStr, P, RepStr, all)).
2✔
174

175
replace(SrcStr, P, RepStr, <<"all">>) when is_binary(SrcStr), is_binary(P), is_binary(RepStr) ->
176
    iolist_to_binary(string:replace(SrcStr, P, RepStr, all));
1✔
177
replace(SrcStr, P, RepStr, <<"trailing">>) when
178
    is_binary(SrcStr), is_binary(P), is_binary(RepStr)
179
->
180
    iolist_to_binary(string:replace(SrcStr, P, RepStr, trailing));
1✔
181
replace(SrcStr, P, RepStr, <<"leading">>) when is_binary(SrcStr), is_binary(P), is_binary(RepStr) ->
182
    iolist_to_binary(string:replace(SrcStr, P, RepStr, leading)).
1✔
183

184
regex_match(Str, RE) ->
185
    case re:run(Str, RE, [global, {capture, none}]) of
7✔
186
        match -> true;
4✔
187
        nomatch -> false
3✔
188
    end.
189

190
regex_replace(SrcStr, RE, RepStr) ->
191
    re:replace(SrcStr, RE, RepStr, [global, {return, binary}]).
3✔
192

193
ascii(Char) when is_binary(Char) ->
194
    [FirstC | _] = binary_to_list(Char),
2✔
195
    FirstC.
2✔
196

197
find(S, P) when is_binary(S), is_binary(P) ->
198
    find_s(S, P, leading).
4✔
199

200
find(S, P, <<"trailing">>) when is_binary(S), is_binary(P) ->
201
    find_s(S, P, trailing);
4✔
202
find(S, P, <<"leading">>) when is_binary(S), is_binary(P) ->
203
    find_s(S, P, leading).
1✔
204

205
find_s(S, P, Dir) ->
206
    case string:find(S, P, Dir) of
9✔
207
        nomatch -> <<"">>;
4✔
208
        SubStr -> SubStr
5✔
209
    end.
210

211
join_to_string(List) when is_list(List) ->
212
    join_to_string(<<", ">>, List).
4✔
213

214
join_to_string(Sep, List) when is_list(List), is_binary(Sep) ->
215
    iolist_to_binary(lists:join(Sep, [str(Item) || Item <- List])).
10✔
216

217
unescape(Bin) when is_binary(Bin) ->
218
    UnicodeList = unicode:characters_to_list(Bin, utf8),
29✔
219
    UnescapedUnicodeList = unescape_string(UnicodeList),
29✔
220
    UnescapedUTF8Bin = unicode:characters_to_binary(UnescapedUnicodeList, utf32, utf8),
27✔
221
    case UnescapedUTF8Bin of
27✔
222
        Out when is_binary(Out) ->
223
            Out;
26✔
224
        Error ->
225
            throw({invalid_unicode_character, Error})
1✔
226
    end.
227

228
nth(N, List) when (is_list(N) orelse is_binary(N)) andalso is_list(List) ->
229
    try binary_to_integer(iolist_to_binary(N)) of
3✔
230
        N1 ->
231
            nth(N1, List)
2✔
232
    catch
233
        _:_ ->
234
            throw(#{reason => invalid_argument, func => nth, index => N})
1✔
235
    end;
236
nth(N, List) when is_integer(N) andalso is_list(List) ->
237
    case length(List) of
2✔
238
        L when L < N -> <<>>;
1✔
239
        _ -> lists:nth(N, List)
1✔
240
    end.
241

242
unescape_string(Input) -> unescape_string(Input, []).
29✔
243

244
unescape_string([], Acc) ->
245
    lists:reverse(Acc);
27✔
246
unescape_string([$\\, $\\ | Rest], Acc) ->
247
    unescape_string(Rest, [$\\ | Acc]);
1✔
248
unescape_string([$\\, $n | Rest], Acc) ->
249
    unescape_string(Rest, [$\n | Acc]);
2✔
250
unescape_string([$\\, $t | Rest], Acc) ->
251
    unescape_string(Rest, [$\t | Acc]);
2✔
252
unescape_string([$\\, $r | Rest], Acc) ->
253
    unescape_string(Rest, [$\r | Acc]);
1✔
254
unescape_string([$\\, $b | Rest], Acc) ->
255
    unescape_string(Rest, [$\b | Acc]);
1✔
256
unescape_string([$\\, $f | Rest], Acc) ->
257
    unescape_string(Rest, [$\f | Acc]);
1✔
258
unescape_string([$\\, $v | Rest], Acc) ->
259
    unescape_string(Rest, [$\v | Acc]);
1✔
260
unescape_string([$\\, $' | Rest], Acc) ->
261
    unescape_string(Rest, [$\' | Acc]);
1✔
262
unescape_string([$\\, $" | Rest], Acc) ->
263
    unescape_string(Rest, [$\" | Acc]);
1✔
264
unescape_string([$\\, $? | Rest], Acc) ->
265
    unescape_string(Rest, [$\? | Acc]);
1✔
266
unescape_string([$\\, $a | Rest], Acc) ->
267
    unescape_string(Rest, [$\a | Acc]);
1✔
268
%% Start of HEX escape code
269
unescape_string([$\\, $x | [$0 | _] = HexStringStart], Acc) ->
270
    unescape_handle_hex_string(HexStringStart, Acc);
7✔
271
unescape_string([$\\, $x | [$1 | _] = HexStringStart], Acc) ->
272
    unescape_handle_hex_string(HexStringStart, Acc);
1✔
273
unescape_string([$\\, $x | [$2 | _] = HexStringStart], Acc) ->
274
    unescape_handle_hex_string(HexStringStart, Acc);
2✔
275
unescape_string([$\\, $x | [$3 | _] = HexStringStart], Acc) ->
NEW
276
    unescape_handle_hex_string(HexStringStart, Acc);
×
277
unescape_string([$\\, $x | [$4 | _] = HexStringStart], Acc) ->
278
    unescape_handle_hex_string(HexStringStart, Acc);
5✔
279
unescape_string([$\\, $x | [$5 | _] = HexStringStart], Acc) ->
280
    unescape_handle_hex_string(HexStringStart, Acc);
1✔
281
unescape_string([$\\, $x | [$6 | _] = HexStringStart], Acc) ->
282
    unescape_handle_hex_string(HexStringStart, Acc);
4✔
283
unescape_string([$\\, $x | [$7 | _] = HexStringStart], Acc) ->
NEW
284
    unescape_handle_hex_string(HexStringStart, Acc);
×
285
unescape_string([$\\, $x | [$8 | _] = HexStringStart], Acc) ->
NEW
286
    unescape_handle_hex_string(HexStringStart, Acc);
×
287
unescape_string([$\\, $x | [$9 | _] = HexStringStart], Acc) ->
NEW
288
    unescape_handle_hex_string(HexStringStart, Acc);
×
289
unescape_string([$\\, $x | [$A | _] = HexStringStart], Acc) ->
NEW
290
    unescape_handle_hex_string(HexStringStart, Acc);
×
291
unescape_string([$\\, $x | [$B | _] = HexStringStart], Acc) ->
NEW
292
    unescape_handle_hex_string(HexStringStart, Acc);
×
293
unescape_string([$\\, $x | [$C | _] = HexStringStart], Acc) ->
NEW
294
    unescape_handle_hex_string(HexStringStart, Acc);
×
295
unescape_string([$\\, $x | [$D | _] = HexStringStart], Acc) ->
NEW
296
    unescape_handle_hex_string(HexStringStart, Acc);
×
297
unescape_string([$\\, $x | [$E | _] = HexStringStart], Acc) ->
NEW
298
    unescape_handle_hex_string(HexStringStart, Acc);
×
299
unescape_string([$\\, $x | [$F | _] = HexStringStart], Acc) ->
300
    unescape_handle_hex_string(HexStringStart, Acc);
1✔
301
unescape_string([$\\, $x | [$a | _] = HexStringStart], Acc) ->
NEW
302
    unescape_handle_hex_string(HexStringStart, Acc);
×
303
unescape_string([$\\, $x | [$b | _] = HexStringStart], Acc) ->
NEW
304
    unescape_handle_hex_string(HexStringStart, Acc);
×
305
unescape_string([$\\, $x | [$c | _] = HexStringStart], Acc) ->
NEW
306
    unescape_handle_hex_string(HexStringStart, Acc);
×
307
unescape_string([$\\, $x | [$d | _] = HexStringStart], Acc) ->
NEW
308
    unescape_handle_hex_string(HexStringStart, Acc);
×
309
unescape_string([$\\, $x | [$e | _] = HexStringStart], Acc) ->
NEW
310
    unescape_handle_hex_string(HexStringStart, Acc);
×
311
unescape_string([$\\, $x | [$f | _] = HexStringStart], Acc) ->
NEW
312
    unescape_handle_hex_string(HexStringStart, Acc);
×
313
%% We treat all other escape sequences as not valid input to leave room for
314
%% extending the function to support more escape codes
315
unescape_string([$\\, X | _Rest], _Acc) ->
316
    erlang:throw({unrecognized_escape_sequence, list_to_binary([$\\, X])});
2✔
317
unescape_string([First | Rest], Acc) ->
318
    unescape_string(Rest, [First | Acc]).
57✔
319

320
unescape_handle_hex_string(HexStringStart, Acc) ->
321
    {RemainingString, Num} = parse_hex_string(HexStringStart),
21✔
322
    unescape_string(RemainingString, [Num | Acc]).
21✔
323

324
parse_hex_string(SeqStartingWithHexDigit) ->
325
    parse_hex_string(SeqStartingWithHexDigit, []).
21✔
326

327
parse_hex_string([], Acc) ->
328
    ReversedAcc = lists:reverse(Acc),
11✔
329
    {[], list_to_integer(ReversedAcc, 16)};
11✔
330
parse_hex_string([First | Rest] = String, Acc) ->
331
    case is_hex_digit(First) of
76✔
332
        true ->
333
            parse_hex_string(Rest, [First | Acc]);
66✔
334
        false ->
335
            ReversedAcc = lists:reverse(Acc),
10✔
336
            {String, list_to_integer(ReversedAcc, 16)}
10✔
337
    end.
338

339
is_hex_digit($0) -> true;
21✔
340
is_hex_digit($1) -> true;
6✔
341
is_hex_digit($2) -> true;
3✔
342
is_hex_digit($3) -> true;
1✔
343
is_hex_digit($4) -> true;
9✔
344
is_hex_digit($5) -> true;
3✔
345
is_hex_digit($6) -> true;
6✔
346
is_hex_digit($7) -> true;
3✔
347
is_hex_digit($8) -> true;
1✔
NEW
348
is_hex_digit($9) -> true;
×
349
is_hex_digit($A) -> true;
5✔
NEW
350
is_hex_digit($B) -> true;
×
351
is_hex_digit($C) -> true;
2✔
NEW
352
is_hex_digit($D) -> true;
×
353
is_hex_digit($E) -> true;
1✔
354
is_hex_digit($F) -> true;
2✔
NEW
355
is_hex_digit($a) -> true;
×
NEW
356
is_hex_digit($b) -> true;
×
357
is_hex_digit($c) -> true;
2✔
NEW
358
is_hex_digit($d) -> true;
×
NEW
359
is_hex_digit($e) -> true;
×
360
is_hex_digit($f) -> true;
1✔
361
is_hex_digit(_) -> false.
10✔
362

363
%%------------------------------------------------------------------------------
364
%% Data Type Conversion Funcs
365
%%------------------------------------------------------------------------------
366

367
str(Data) ->
368
    emqx_utils_conv:bin(Data).
48✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc