Current section

Files

Jump to
exosql lib format.ex
Raw

lib/format.ex

require Logger
defmodule ExoSQL.Format do
@format_re ~r/%[\d\.,\-+]*[%fsdk]/
@format_re_one ~r/([\d\.,\-+]*)([fsdk])/
def compile_format(str) do
Regex.split(@format_re, str, include_captures: true)
|> Enum.reduce([], fn
"%%", acc ->
["%" | acc]
"%" <> fs, acc ->
[_all, mod, type] = Regex.run(@format_re_one, fs)
[{mod, type} | acc]
"", acc ->
acc
other, acc ->
[other | acc]
end)
end
def format(str, params) when is_binary(str) do
# Logger.debug("Compile str #{inspect str}")
precompiled = compile_format(str)
# Logger.debug("Precompiled: #{inspect precompiled}")
format(precompiled, params)
end
def format([], _params), do: ""
def format(precompiled, params) when is_list(precompiled) do
# Logger.debug("Format #{inspect {precompiled, params}}")
res =
Enum.reduce(precompiled, {[], Enum.reverse(params)}, fn
{mod, type}, {acc, params} ->
[head | rest] = params
repl = format_one(mod, type, head)
{[repl | acc], rest}
str, {acc, params} ->
{[str | acc], params}
end)
# Logger.debug("Result #{inspect {precompiled, params}} -> #{inspect res}")
case res do
{str, []} ->
to_string(str)
{_str, other} ->
throw({:error, {:format, {:pending, Enum.count(other)}}})
end
end
def localized_number(number) do
localized_number(number, 0)
end
def localized_number(number, 0) do
int = :erlang.float_to_binary(Float.floor(number), decimals: 0)
head = localized_number_str_comma(int)
"#{head}"
end
def localized_number(number, decimals) do
dec = :erlang.float_to_binary(number, decimals: decimals)
{int, dec} = String.split_at(dec, String.length(dec) - decimals)
head = localized_number_str_comma(String.slice(int, 0, String.length(int) - 1))
if dec == String.duplicate("0", decimals) do
"#{head}"
else
"#{head},#{dec}"
end
end
defp localized_number_str_comma(lit) do
if String.length(lit) > 3 do
{head, tail} = String.split_at(lit, String.length(lit) - 3)
head = localized_number_str_comma(head)
"#{head}.#{tail}"
else
lit
end
end
def format_one(mod, type, data) do
# to_string(data)
case {mod, type} do
{"", "s"} ->
to_string(data)
{"-" <> count, "s"} ->
data = to_string(data)
count = ExoSQL.Utils.to_number!(count) - String.length(data)
if count > 0 do
data <> String.duplicate(" ", count)
else
data
end
{count, "s"} ->
data = to_string(data)
count = ExoSQL.Utils.to_number!(count) - String.length(data)
if count > 0 do
String.duplicate(" ", count) <> data
else
data
end
{"", "f"} ->
{:ok, data} = ExoSQL.Utils.to_float(data)
:erlang.float_to_binary(data, decimals: 2)
{"." <> decimals, "f"} ->
{:ok, data} = ExoSQL.Utils.to_float(data)
{:ok, decimals} = ExoSQL.Utils.to_number(decimals)
:erlang.float_to_binary(data, decimals: decimals)
{"+", "f"} ->
{:ok, datan} = ExoSQL.Utils.to_float(data)
data = :erlang.float_to_binary(datan, decimals: 2)
if datan <= 0 do
"#{data}"
else
"+#{data}"
end
{"", "d"} ->
{:ok, data} = ExoSQL.Utils.to_number(data)
data = Kernel.trunc(data)
"#{data}"
{"+", "d"} ->
datan = ExoSQL.Utils.to_number!(data)
datan = Kernel.trunc(datan)
if datan <= 0 do
"#{data}"
else
"+#{data}"
end
{<<fill::size(8)>> <> count, "d"} ->
{:ok, data} = ExoSQL.Utils.to_number(data)
data = Kernel.trunc(data)
data = "#{data}"
count = ExoSQL.Utils.to_number!(count) - String.length(data)
if count > 0 do
String.duplicate(<<fill::utf8>>, count) <> data
else
data
end
{".", "k"} ->
{:ok, data} = ExoSQL.Utils.to_float(data)
{data, sufix} =
cond do
data >= 1_000_000 ->
data = data / 1_000_000
data = :erlang.float_to_binary(data, decimals: 1)
{data, "M"}
data >= 100_000 ->
data = data / 1_000
data = :erlang.float_to_binary(data, decimals: 1)
{data, "K"}
data >= 1_000 ->
data = Kernel.trunc(data)
{data, ""}
true ->
data = :erlang.float_to_binary(data, decimals: 2)
{data, ""}
end
"#{data}#{sufix}"
{",", "k"} ->
{:ok, data} = ExoSQL.Utils.to_float(data)
{data, sufix} =
cond do
data >= 1_000_000 ->
data = data / 1_000_000
data = localized_number(data, 1)
{data, "MM"}
data >= 100_000 ->
data = data / 1_000
data = localized_number(data, 1)
{data, "K"}
data >= 1_000 ->
data = localized_number(data, 0)
{data, ""}
true ->
data = localized_number(data, 2)
{data, ""}
end
"#{data}#{sufix}"
{"", "k"} ->
{:ok, data} = ExoSQL.Utils.to_number(data)
data = Kernel.trunc(data)
{data, sufix} =
cond do
data >= 1_000_000 ->
{div(data, 1_000_000), "M"}
data >= 1_000 ->
{div(data, 1_000), "k"}
true ->
{data, ""}
end
"#{data}#{sufix}"
end
end
end