Current section

Files

Jump to
ex_dinheiro lib moeda.ex
Raw

lib/moeda.ex

defmodule Moeda do
@moduledoc """
Documentation for Moeda.
"""
@moedas %{
BRL: %{nome: "Brazilian Real", simbolo: "R$", codigo: "BRL", codigo_iso: 986, expoente: 2},
USD: %{nome: "US Dollar", simbolo: "$", codigo: "USD", codigo_iso: 840, expoente: 2}
}
@spec find(String.t() | atom) :: map | nil
@doc """
Return a map from an atom or string that represents an ISO 4217 code.
## Examples
iex> Moeda.find(:BRL)
%{nome: "Brazilian Real", simbolo: "R$", codigo: "BRL", codigo_iso: 986, expoente: 2}
iex> Moeda.find("BRL")
%{nome: "Brazilian Real", simbolo: "R$", codigo: "BRL", codigo_iso: 986, expoente: 2}
iex> Moeda.find("")
nil
Its function ignore case sensitive.
## Examples
iex> Moeda.find(:brl)
%{nome: "Brazilian Real", simbolo: "R$", codigo: "BRL", codigo_iso: 986, expoente: 2}
iex> Moeda.find("brl")
%{nome: "Brazilian Real", simbolo: "R$", codigo: "BRL", codigo_iso: 986, expoente: 2}
"""
def find(codigo) when is_atom(codigo) do
codigo
|> Atom.to_string()
|> String.upcase()
|> String.to_atom()
|> findp
end
def find(codigo) when is_binary(codigo) do
codigo
|> String.upcase()
|> String.to_atom()
|> findp
end
defp findp(codigo) do
@moedas[codigo]
end
@spec get_atom(String.t() | atom) :: atom | nil
@doc """
Return an atom from a value that represents an ISO 4217 code.
## Examples
iex> Moeda.get_atom(:BRL)
:BRL
iex> Moeda.get_atom("BRL")
:BRL
iex> Moeda.get_atom("")
nil
Its function ignore case sensitive.
## Examples
iex> Moeda.get_atom(:brl)
:BRL
iex> Moeda.get_atom("brl")
:BRL
"""
def get_atom(codigo) do
moeda = find(codigo)
if moeda do
moeda.codigo |> String.upcase() |> String.to_atom()
else
nil
end
end
@spec get_factor(String.t() | atom) :: float | nil
@doc """
Return a multiplication factor from an ISO 4217 code.
## Examples
iex> Moeda.get_factor(:BRL)
100.0
iex> Moeda.get_factor("BRL")
100.0
iex> Moeda.get_factor("")
nil
Its function ignore case sensitive.
## Examples
iex> Moeda.get_factor(:brl)
100.0
iex> Moeda.get_factor("brl")
100.0
"""
def get_factor(codigo) do
moeda = find(codigo)
if moeda do
:math.pow(10, moeda.expoente)
else
nil
end
end
@spec to_string(String.t() | atom, float, Keywords.t()) :: String.t()
@doc """
Return a formated string from a ISO 4217 code and a float value.
## Examples
iex> Moeda.to_string(:BRL, 100.0)
"R$ 100,00"
iex> Moeda.to_string("BRL", 1000.5)
"R$ 1.000,50"
iex> Moeda.to_string(:BRL, -1.0)
"R$ -1,00"
Its function ignore case sensitive.
## Examples
iex> Moeda.to_string(:bRl, 100.0)
"R$ 100,00"
iex> Moeda.to_string("BrL", 1000.5)
"R$ 1.000,50"
Using options-style parameters you can change the behavior of the function.
- `thousand_separator` - default `"."`, sets the thousand separator.
- `decimal_separator` - default `","`, sets the decimal separator.
## Exemples
iex> Moeda.to_string(:USD, 1000.5, thousand_separator: ",", decimal_separator: ".")
"$ 1,000.50"
"""
def to_string(moeda, valor, opts \\ []) do
m = Moeda.find(moeda)
unless m, do: raise(ArgumentError, message: "'#{moeda}' does not represent an ISO 4217 code.")
unless is_float(valor), do: raise(ArgumentError, message: "Value '#{valor}' must be float.")
thousand_separator = Keyword.get(opts, :thousand_separator, ".")
decimal_separator = Keyword.get(opts, :decimal_separator, ",")
parts =
valor
|> :erlang.float_to_binary(decimals: m.expoente)
|> String.split(".")
thousands =
parts
|> List.first()
|> String.reverse()
|> String.codepoints()
|> format_thousands(thousand_separator)
|> String.reverse()
decimals =
if m.expoente > 0 do
Enum.join([decimal_separator, List.last(parts)])
else
""
end
[m.simbolo, " ", thousands, decimals]
|> Enum.join()
|> String.trim()
end
defp format_thousands([head | tail], separator, opts \\ []) do
position = Keyword.get(opts, :position, 1)
num =
if rem(position, 3) == 0 and head != "-" and tail != [] do
Enum.join([head, separator])
else
head
end
if tail != [] do
[num, format_thousands(tail, separator, position: position + 1)]
|> Enum.join()
else
num
end
end
end