Packages

A common set of utilities for Elixir/Phoenix applications

Current section

Files

Jump to
extract lib parsers float_parser.ex
Raw

lib/parsers/float_parser.ex

defmodule Parsers.FloatParser do
@moduledoc "Parse input data for real numbers"
alias Parsers.ValidationError
@doc """
Parse input data for float.
Returns a tuple with `:ok` or `:error`, a list of messages (used only if returns `:error`) and `struct`.
`struct` is previous struct. Can be map or tuple, anything else do nothing.
`param_name` is key name. Can be atom or string.
`params` is map with input data. Need to be a map.
`restrictions` is keywords with restrictions of input. Can contains:
* `:default` - will put input data with this value if input data is nil or empty string.
* `:max` - the max value. Input data need to be equals or upper of this value. Need to be float or integer number.
* `:min` - the min value. Input data need to be equals or lower of this value. Need to be float or integer number.
* `:required` - if `params` map needs to have input value with valid float.
### Examples
Just call:
```
iex> Parsers.FloatParser.parse(%{}, :float, %{float: "1.0"})
{:ok, [], %{float: 1.0}}
# With integer value
iex> Parsers.FloatParser.parse(%{}, :float, %{float: "1"})
{:ok, [], %{float: 1}}
# With default value
iex> Parsers.FloatParser.parse(%{other: 123}, :float, %{float: ""}, default: "1.0")
{:ok, [], %{float: 1.0, other: 123}}
# If input data is invalid
iex> Parsers.FloatParser.parse(%{other: 123}, :float, %{float: "asdsa"})
{:error, ["invalid_float"], %{other: 123}}
# If input data is required and not provider
iex> Parsers.FloatParser.parse(%{other: 123}, :float, %{}, required: true)
{:error, ["float_not_provided"], %{other: 123}}
```
"""
@doc since: "0.1.0"
@spec parse(Map | Tuple, String | Atom, Map, Keywords | nil) ::
{:ok, [], struct} | {:error, [String], struct}
def parse(struct, param_name, params, restrictions \\ [])
def parse(struct, param_name, params, restrictions) when is_map(struct),
do: parse({:ok, [], struct}, param_name, params, restrictions)
def parse(input, param_name, params, restrictions) when is_tuple(input) do
case Map.get(params, param_name) do
val when val in [nil, ""] -> bind_if_valid(input, param_name, nil, restrictions)
val when is_binary(val) -> str_to_float(input, param_name, val, restrictions)
val -> bind_if_valid(input, param_name, val, restrictions)
end
end
defp str_to_float(input, param_name, val, restrictions) do
case Float.parse(val) do
{parsed_number, ""} -> bind_if_valid(input, param_name, parsed_number, restrictions)
_ -> ValidationError.add(input, "invalid_#{param_name}")
end
end
defp bind_if_valid(input, param_name, nil, [default: val] = restrictions) do
bind_if_valid(input, param_name, val, restrictions)
end
defp bind_if_valid(input, param_name, val, restrictions) do
input
|> check_restrictions(param_name, val, Enum.into(restrictions, %{}))
|> set_if_no_errors(param_name, val)
end
defp check_restrictions(input, param_name, val, restrictions) do
input
|> check_required(param_name, val, restrictions)
|> check_lower_bound(param_name, val, restrictions)
|> check_upper_bound(param_name, val, restrictions)
end
defp check_required(input, param_name, nil, %{required: true}) do
ValidationError.add(input, "#{param_name}_not_provided")
end
defp check_required(input, _param_name, _val, _restrictions) do
input
end
defp check_upper_bound(input, param_name, val, %{max: max}) when is_float(val) or is_integer(val) do
case val <= max do
true -> input
_ -> ValidationError.add(input, "invalid_#{param_name}")
end
end
defp check_upper_bound(input, _param_name, _val, _restrictions) do
input
end
defp check_lower_bound(input, param_name, val, %{min: min}) when is_float(val) or is_integer(val) do
case val >= min do
true -> input
_ -> ValidationError.add(input, "invalid_#{param_name}")
end
end
defp check_lower_bound(input, _param_name, _val, _restrictions) do
input
end
defp set_if_no_errors({:ok, msgs, struct}, param_name, val) when (is_float(val) or is_integer(val)) and is_atom(param_name) do
{:ok, msgs, Map.put(struct, param_name, val) }
end
defp set_if_no_errors({:ok, msgs, struct}, param_name, val) when is_float(val) or is_integer(val) do
{:ok, msgs, Map.put(struct, String.to_atom(param_name), val) }
end
defp set_if_no_errors(input, _param_name, _val), do: input
end