-module(eFmtFormat). -include("eFmt.hrl"). -compile(export_all). %% Formatting functions of io library. -export([ fwrite/2 , fwrite/3 , floatG/1 , scan/2 , build/1 , build/2 ]). %% 在字符串格式之后将参数格式化为Args。刚产生 %% 如果参数中有错误,则为错误。 %% %% 要正确执行打印命令,我们需要计算 %% 当前缩进的所有内容。这可能非常 %% 价格昂贵,尤其是在不需要时,因此我们首先确定 %% 是否以及需要多长时间来计算缩进。我们的确是 %% 首先收集所有控制序列,然后 %% 相应的参数,然后计算打印顺序,然后 %% 然后构建输出。这种方法有一些缺点,它确实 %% 在格式字符串上两次传递并创建更多临时数据, %% 并且还将控制字符的处理分为两个 %% 部分。 -spec fwrite(Format :: io:format(), Data :: [term()]) -> eFmt:chars(). fwrite(Format, Args) -> build(scan(Format, Args)). -spec fwrite(Format :: io:format(), Data :: [term()], Options :: [{'chars_limit', CharsLimit :: integer()}]) -> eFmt:chars(). fwrite(Format, Args, Options) -> build(scan(Format, Args), Options). %% Parse all control sequences in the format string. -spec scan(Format :: io:format(), Data :: [term()]) -> FormatList :: [char() | eFmt:fmtSpec()]. %% 格式 ~F.P.PadModC scan(Format, Args) -> if is_atom(Format) -> doCollect(atom_to_binary(Format, utf8), Args, []); is_list(Format) -> doCollect(list_to_binary(Format), Args, []); true -> doCollect(Format, Args, []) end. doCollect(FmtBinStr, Args, Acc) -> case binary:split(FmtBinStr, <<"~">>) of [NotMatch] -> [NotMatch | Acc]; [FPart, LPart] -> doCollWidth(LPart, Args, 0, left, [FPart | Acc]) end. doCollWidth(<<>>, _Args, _Width, _Adjust, Acc) -> Acc; doCollWidth(LPart, Args, Width, Adjust, Acc) -> %% 匹配宽度 case LPart of <<"-*", LeftLPart/binary>> -> [WidthArgs | LeftArgs] = Args, doCollPrecision(LeftLPart, LeftArgs, WidthArgs, left, Acc); <<"-", LeftLPart/binary>> -> doCollWidth(LeftLPart, Args, Width, left, Acc); <<"*", LeftLPart/binary>> -> [WidthArgs | LeftArgs] = Args, doCollPrecision(LeftLPart, LeftArgs, WidthArgs, right, Acc); <> -> case WidthInt >= $0 andalso WidthInt =< $9 of true -> doCollWidth(LeftLPart, Args, 10 * Width + (WidthInt - $0), Adjust, Acc); _ -> case Width == 0 of true -> doCollPrecision(LPart, Args, none, Adjust, Acc); _ -> doCollPrecision(LPart, Args, Width, Adjust, Acc) end end end. doCollPrecision(LPart, Args, Width, Adjust, Acc) -> case LPart of <<".", LeftLPart/binary>> -> doCollPrecision(LeftLPart, Args, Width, Adjust, 0, Acc); _ -> doCollPadChar(LPart, Args, Width, Adjust, none, Acc) end. doCollPrecision(LPart, Args, Width, Adjust, Precision, Acc) -> case LPart of <<"*", LeftLPart/binary>> -> [PrecisionArgs | LeftArgs] = Args, doCollPadChar(LeftLPart, LeftArgs, Width, Adjust, PrecisionArgs, Acc); <> -> case PrecisionInt >= $0 andalso PrecisionInt =< $9 of true -> doCollPrecision(LeftLPart, Args, Width, Adjust, 10 * Precision + (PrecisionInt - $0)); _ -> case Precision == 0 of true -> doCollPadChar(LPart, Args, Width, Adjust, none, Acc); _ -> doCollPadChar(LPart, Args, Width, Adjust, Precision, Acc) end end end. doCollPadChar(LPart, Args, Width, Adjust, Precision, Acc) -> case LPart of <<".*", LeftLPart/binary>> -> [PadChar | LeftArgs] = Args, doCollEncoding(LeftLPart, LeftArgs, Width, Adjust, Precision, PadChar, Acc); <<".", PadChar:8/integer, LeftLPart/binary>> -> doCollEncoding(LeftLPart, Args, Width, Adjust, Precision, PadChar, Acc); _ -> doCollEncoding(LPart, Args, Width, Adjust, Precision, 32, Acc) end. doCollEncoding(LPart, Args, Width, Adjust, Precision, PadChar, Acc) -> case LPart of <<"t", LeftLPart/binary>> -> %true = Char =/= $l, doCollStrings(LeftLPart, Args, Width, Adjust, Precision, PadChar, unicode, Acc); _ -> doCollStrings(LPart, Args, Width, Adjust, Precision, PadChar, latin1, Acc) end. doCollStrings(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, Acc) -> case LPart of <<"l", LeftLPart/binary>> -> %true = Char =/= $t, doCollCA(LeftLPart, Args, Width, Adjust, Precision, PadChar, Encoding, false, Acc); _ -> doCollCA(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, true, Acc) end. doCollCA(LPart, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, Acc) -> <> = LPart, case CtlChar of $w -> [OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $p ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $W ->[OneArgs | LeftArgs] = Args, [Depth | LastArgs] = LeftArgs, As = [OneArgs, Depth], NextArgs = LastArgs; $P -> [OneArgs | LeftArgs] = Args,[Depth | LastArgs] = LeftArgs, As = [OneArgs, Depth], NextArgs = LastArgs; $s ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $e ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $f ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $g -> [OneArgs | LeftArgs] = Args,As = OneArgs, NextArgs = LeftArgs; $b ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $B ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $x ->[OneArgs | LeftArgs] = Args, [Prefix | LastArgs] = LeftArgs, As = [OneArgs, Prefix], NextArgs = LastArgs; $X -> [OneArgs | LeftArgs] = Args,[Prefix | LastArgs] = LeftArgs, As = [OneArgs, Prefix], NextArgs = LastArgs; $+ -> [OneArgs | LeftArgs] = Args,As = OneArgs, NextArgs = LeftArgs; $# ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $c ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs; $~ -> As = undefined, NextArgs = Args; $n -> As = undefined, NextArgs = Args; $i ->[OneArgs | LeftArgs] = Args, As = OneArgs, NextArgs = LeftArgs end, FmtSpec = #fmtSpec{ctlChar = CtlChar, args = As, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings}, doCollect(LeftLPart, NextArgs, [FmtSpec | Acc]). %% Build the output text for a pre-parsed format list. -spec build(FormatList :: [char() | eFmt:fmtSpec()]) -> eFmt:chars(). build(Cs) -> build(Cs, []). -spec build(FormatList :: [char() | eFmt:fmtSpec()], Options :: [{'chars_limit', CharsLimit :: integer()}]) -> eFmt:chars(). build(Cs, Options) -> CharsLimit = getOpt(chars_limit, Options, -1), ResList = buildSmall(Cs, []), {P, S, W, Other} = cntSmall(ResList, 0, 0, 0, 0), case P + S + W of 0 -> ResList; NumOfLimited -> RemainChars = remainChars(CharsLimit, Other), buildLimited(ResList, P, NumOfLimited, RemainChars, 0, []) end. %% build_small([Control]) -> eFmt:chars(). %% Interpret the control structures, but only the small ones. The big ones are saved for later. %% build_limited([Control], NumberOfPps, NumberOfLimited, CharsLimit, Indentation) %% Interpret the control structures. Count the number of print %% remaining and only calculate indentation when necessary. Must also %% be smart when calculating indentation for characters in format. buildSmall([], Acc) -> Acc; buildSmall([OneCA | Cs], Acc) -> case OneCA of #fmtSpec{ctlChar = CtlChar, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding} -> case ctlSmall(CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding) of not_small -> buildSmall(Cs, [OneCA | Acc]); ignore -> buildSmall(Cs, Acc); Str -> buildSmall(Cs, [Str | Acc]) end; _ -> buildSmall(Cs, [OneCA | Acc]) end. %% control_small(FormatChar, [Argument], FieldWidth, Adjust, Precision, %% PadChar, Encoding) -> String %% control_limited(FormatChar, [Argument], FieldWidth, Adjust, Precision, %% PadChar, Encoding, StringP, ChrsLim, Indentation) -> String %% These are the dispatch functions for the various formatting controls. ctlSmall($s, Args, Width, Adjust, Precision, PadChar, Encoding) when is_atom(Args) -> case Encoding of latin1 -> AtomBinStr = eFmt:writeAtom(Args, latin1); _ -> AtomBinStr = eFmt:writeAtom(Args, uft8) end, string(AtomBinStr, Width, Adjust, Precision, PadChar, Encoding); ctlSmall($e, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_float(Args) -> floatE(Args, Width, Adjust, Precision, PadChar); ctlSmall($f, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_float(Args) -> floatF(Args, Width, Adjust, Precision, PadChar); ctlSmall($g, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_float(Args) -> floatG(Args, Width, Adjust, Precision, PadChar); ctlSmall($b, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> unPrefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, true); ctlSmall($B, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> unPrefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, false); ctlSmall($x, [Args, Prefix], Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args), is_atom(Prefix) -> prefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, atom_to_binary(Prefix, utf8), true); ctlSmall($x, [Args, Prefix], Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> prefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, Prefix, true); ctlSmall($X, [Args, Prefix], Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args), is_atom(Prefix) -> prefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, atom_to_binary(Prefix, utf8), false); ctlSmall($X, [Args, Prefix], Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> prefixedInt(Args, Width, Adjust, ?base(Precision), PadChar, Prefix, false); ctlSmall($+, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> Base = ?base(Precision), prefixedInt(Args, Width, Adjust, Base, PadChar, integer_to_binary(Base), $#, true); ctlSmall($#, Args, Width, Adjust, Precision, PadChar, _Encoding) when is_integer(Args) -> Base = ?base(Precision), prefixedInt(Args, Width, Adjust, Base, PadChar, integer_to_binary(Base), $#, false); ctlSmall($c,Args, Width, Adjust, Precision, PadChar, Encoding) when is_integer(Args) -> case Encoding of unicode -> char(Args, Width, Adjust, Precision, PadChar); _ -> char(Args band 255, Width, Adjust, Precision, PadChar) end; ctlSmall($~, _Args, Width, Adjust, Precision, PadChar, _Encoding) -> char($~, Width, Adjust, Precision, PadChar); ctlSmall($n, _Args, Width, Adjust, Precision, PadChar, _Encoding) -> newline(Width, Adjust, Precision, PadChar); ctlSmall($i, _Args, _Width, _Adjust, _Precision, _PadChar, _Encoding) -> ignore; ctlSmall(_C, _Args, _Width, _Adjust, _Precision, _PadChar, _Encoding) -> not_small. cntSmall([], P, S, W, Other) -> {P, S, W, Other}; cntSmall([OneRes | Cs], P, S, W, Other) -> case OneRes of #fmtSpec{ctlChar = CtlChar} -> case CtlChar of $p -> cntSmall(Cs, P + 1, S, W, Other); $P -> cntSmall(Cs, P + 1, S, W, Other); $w -> cntSmall(Cs, P, S, W + 1, Other); $W -> cntSmall(Cs, P, S, W + 1, Other); $s -> cntSmall(Cs, P, S, W + 1, Other); _ -> cntSmall(Cs, P, S, W, Other) end; _ -> if is_binary(OneRes) orelse is_list(OneRes) -> cntSmall(Cs, P, S, W, Other + eFmt:charsLen(OneRes)); is_integer(OneRes) -> cntSmall(Cs, P, S, W, Other + 1); true -> cntSmall(Cs, P, S, W, Other) end end. buildLimited([], _, _, _, _, Acc) -> Acc; buildLimited([OneCA | Cs], NumOfPs, Count, MaxLen, I, Acc) -> case OneCA of #fmtSpec{ctlChar = CtlChar, args = Args, width = Width, adjust = Adjust, precision = Precision, padChar = PadChar, encoding = Encoding, strings = Strings} -> MaxChars = if MaxLen < 0 -> MaxLen; true -> MaxLen div Count end, IoListStr = ctlLimited(CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I), NewNumOfPs = decrPc(CtlChar, NumOfPs), NewCount = Count - 1, MaxLen = ?IIF(MaxLen < 0, MaxLen, remainChars(MaxLen, eFmt:charsLen(IoListStr))), if NewNumOfPs > 0 -> buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I, [IoListStr | Acc]); true -> buildLimited(Cs, NewNumOfPs, NewCount, MaxLen, I, [IoListStr | Acc]) end; _ -> buildLimited(Cs, NumOfPs, Count, MaxLen, I + 1, [OneCA | Acc]) end. decrPc($p, Pc) -> Pc - 1; decrPc($P, Pc) -> Pc - 1; decrPc(_, Pc) -> Pc. %% (CtlChar, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, MaxChars, I) ctlLimited($s, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> case Encoding of latin1 -> BinStr = erlang:iolist_to_binary(Args); _ -> BinStr = case catch unicode:characters_to_binary(Args, unicode) of Str when is_binary(Str) -> Str; _ -> toBinary(Args) end end, TemBinStr = strToChars(BinStr, Width, CharsLimit), string(TemBinStr, ?IIF(CharsLimit < 0 orelse Width =:= none, Width, max(3, min(Width, CharsLimit))), Adjust, Precision, PadChar, Encoding); ctlLimited($w, Args, Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> Chars = eFmt:doWrite(Args, -1, Encoding, CharsLimit), term(Chars, Width, Adjust, Precision, PadChar); ctlLimited($p, Args, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) -> print(Args, -1, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I); ctlLimited($W, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, _Strings, CharsLimit, _I) -> Chars = eFmt:doWrite(Args, Depth, Encoding, CharsLimit), term(Chars, Width, Adjust, Precision, PadChar); ctlLimited($P, [Args, Depth], Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I) -> print(Args, Depth, Width, Adjust, Precision, PadChar, Encoding, Strings, CharsLimit, I). %% term(TermList, Field, Adjust, Precision, PadChar) %% Output the characters in a term. %% Adjust the characters within the field if length less than Max padding %% with PadChar. term(BinStrOrIoList, Width, Adjust, Precision, PadChar) -> if Width == none andalso Precision == none -> BinStrOrIoList; Width == none -> StrLen = eFmt:charsLen(BinStrOrIoList), NewPrecision = erlang:min(StrLen, Precision), if StrLen > NewPrecision -> adjust(Adjust, makePadChars($*, NewPrecision, <<>>), <<>>); true -> adjust(Adjust, BinStrOrIoList, makePadChars(PadChar, Precision - StrLen, <<>>)) end; true -> StrLen = eFmt:charsLen(BinStrOrIoList), NewPrecision = erlang:min(StrLen, case Precision of none -> Width; _ -> min(Precision, Width) end), if StrLen > NewPrecision -> adjust(Adjust, makePadChars($*, NewPrecision, <<>>), makePadChars(PadChar, Width - NewPrecision, <<>>)); true -> adjust(Adjust, BinStrOrIoList, makePadChars(PadChar, Width - StrLen, <<>>)) end end. %% print(Term, Depth, Field, Adjust, Precision, PadChar, Encoding, %% Indentation) %% Print a term. Field width sets maximum line length, Precision sets %% initial indentation. print(T, D, none, Adj, P, Pad, E, Str, ChLim, I) -> print(T, D, 80, Adj, P, Pad, E, Str, ChLim, I); print(T, D, F, Adj, none, Pad, E, Str, ChLim, I) -> print(T, D, F, Adj, I + 1, Pad, E, Str, ChLim, I); print(T, D, F, right, P, _Pad, Enc, Str, ChLim, _I) -> Options = [{chars_limit, ChLim}, {column, P}, {line_length, F}, {depth, D}, {encoding, Enc}, {strings, Str}], eFmt_pretty:print(T, Options). floatE(Float, Width, Adjust, Precision, PadChar) -> case Precision of none -> NewPrecision = 6; _ -> NewPrecision = Precision end, case Width of none -> float_to_binary(Float, [{scientific, NewPrecision}]); _ -> term(float_to_binary(Float, [{scientific, NewPrecision}]), Width, Adjust, Width, PadChar) end. floatF(Float, Width, Adjust, Precision, PadChar) -> case Precision of none -> NewPrecision = 6; _ -> NewPrecision = Precision end, case Width of none -> float_to_binary(Float, [{decimals, NewPrecision}]); _ -> term(float_to_binary(Float, [{decimals, NewPrecision}]), Width, Adjust, Width, PadChar) end. %% fwrite_g(Float, Field, Adjust, Precision, PadChar) %% Use the f form if Float is >= 0.1 and < 1.0e4, %% and the prints correctly in the f form, else the e form. %% Precision always means the # of significant digits. floatG(Float, Width, Adjust, Precision, PadChar) -> case Float > -10000.0 andalso Float < 10000.0 of true -> floatF(Float, Width, Adjust, Precision, PadChar); _ -> floatE(Float, Width, Adjust, Precision, PadChar) end. floatG(Float) -> float_to_binary(Float, [{decimals, 6}]). strToChars(BinStr, Width, CharsLimit) -> ByteSize = byte_size(BinStr), if Width == none -> case CharsLimit < 0 orelse CharsLimit >= ByteSize of true -> BinStr; _ -> <<(binary:part(BinStr, 0, CharsLimit))/binary, "...">> end; CharsLimit < 0 orelse CharsLimit >= Width -> BinStr; true -> <<(binary:part(BinStr, 0, CharsLimit))/binary, "...">> end. string(Str, Width, Adjust, Precision, PadChar, Encoding) -> if Width == none andalso Precision == none -> Str; Precision == none -> strField(Str, Width, Adjust, eFmt:charsLen(Str), PadChar, Encoding); Width == none -> strField(Str, Precision, left, eFmt:charsLen(Str), PadChar, Encoding); true -> StrLen = eFmt:charsLen(Str), if Width > Precision -> if StrLen > Precision -> adjust(Adjust, flatTrunc(Str, Precision, Encoding), makePadChars(PadChar, Width - Precision, <<>>)); StrLen < Precision -> adjust(Adjust, [Str | makePadChars(PadChar, Precision - StrLen, <<>>)], makePadChars(PadChar, Width - Precision, <<>>)); true -> % N == P adjust(Adjust, Str, makePadChars(PadChar, Width - Precision, <<>>)) end; true -> % F == P strField(Str, Width, Adjust, StrLen, PadChar, Encoding) end end. strField(Str, Width, Adjust, StrLen, PadChar, Encoding) when StrLen > Width -> if StrLen > Width -> flatTrunc(Str, Width, Encoding); StrLen < Width -> adjust(Adjust, Str, makePadChars(PadChar, Width - StrLen, <<>>)); true -> Str end. flatTrunc(List, Width, _Encoding) -> binary:part(iolist_to_binary(List), 0, Width). makePadChars(Char, Cnt, BinStr) -> case Cnt > 0 of true -> makePadChars(Cnt - 1, Char, <>); _ -> BinStr end. adjust(left, Data, Pad) -> [Data, Pad]; adjust(right, Data, Pad) -> [Pad, Data]. unPrefixedInt(Int, Width, Adjust, Base, PadChar, Lowercase) -> case Lowercase of true -> term(toLowerStr(integer_to_binary(Int, Base)), Width, Adjust, none, PadChar); _ -> term(integer_to_binary(Int, Base), Width, Adjust, none, PadChar) end. prefixedInt(Int, Width, Adjust, Base, PadChar, Prefix, Lowercase) -> case Int < 0 of true -> case Lowercase of true -> term(<<"-", (toBinary(Prefix))/binary, (toLowerStr(integer_to_binary(-Int, Base)))/binary>>, Width, Adjust, none, PadChar); _ -> term(<<"-", (toBinary(Prefix))/binary, (integer_to_binary(-Int, Base))/binary>>, Width, Adjust, none, PadChar) end; _ -> case Lowercase of true -> term(<<(toBinary(Prefix))/binary, (toLowerStr(integer_to_binary(Int, Base)))/binary>>, Width, Adjust, none, PadChar); _ -> term(<<(toBinary(Prefix))/binary, (integer_to_binary(Int, Base))/binary>>, Width, Adjust, none, PadChar) end end. prefixedInt(Int, Width, Adjust, Base, PadChar, Prefix, Prefix2, Lowercase) -> case Int < 0 of true -> case Lowercase of true -> term(<<"-", (toBinary(Prefix))/binary, Prefix2:8, (toLowerStr(integer_to_binary(-Int, Base)))/binary>>, Width, Adjust, none, PadChar); _ -> term(<<"-", (toBinary(Prefix))/binary, Prefix2:8, (integer_to_binary(-Int, Base))/binary>>, Width, Adjust, none, PadChar) end; _ -> case Lowercase of true -> term(<<(toBinary(Prefix))/binary, Prefix2:8, (toLowerStr(integer_to_binary(Int, Base)))/binary>>, Width, Adjust, none, PadChar); _ -> term(<<(toBinary(Prefix))/binary, Prefix2:8, (integer_to_binary(Int, Base))/binary>>, Width, Adjust, none, PadChar) end end. char(Char, Width, Adjust, Precision, PadChar) -> if Width == none andalso Precision == none -> integer_to_binary(Char); Precision == none -> makePadChars(Char, Width, <<>>); Width == none -> makePadChars(Char, Precision, <<>>); true -> adjust(Adjust, makePadChars(Char, Precision, <<>>), makePadChars(PadChar, Width - Precision, <<>>)) end. newline(none, _Adjust, _Precision, _PadChar) -> <<"\n">>; newline(Width, Adjust, _Precision, _PadChar) -> case Adjust of right -> makePadChars($\n, Width, <<>>); _ -> <<"\n">> end. remainChars(T, E) -> if T < 0 -> T; T >= E -> T - E; true -> 0 end. getOpt(Key, TupleList, Default) -> case lists:keyfind(Key, 1, TupleList) of {_, Value} -> Value; _ -> Default end. toLowerStr(BinStr) -> << begin case C >= $A andalso C =< $Z of true -> <<(C + 32)>>; _ -> <> end end || <> <= BinStr >>. toUpperStr(BinStr) -> << begin case C >= $a andalso C =< $z of true -> <<(C - 32)>>; _ -> <> end end || <> <= BinStr >>. toBinary(Value) when is_integer(Value) -> integer_to_binary(Value); toBinary(Value) when is_list(Value) -> list_to_binary(Value); toBinary(Value) when is_float(Value) -> float_to_binary(Value, [{decimals, 6}, compact]); toBinary(Value) when is_atom(Value) -> atom_to_binary(Value, utf8); toBinary(Value) when is_binary(Value) -> Value; toBinary([Tuple | PropList] = Value) when is_list(PropList) and is_tuple(Tuple) -> lists:map(fun({K, V}) -> {toBinary(K), toBinary(V)} end, Value); toBinary(Value) -> term_to_binary(Value).