exercism/elixir/city-office/lib/form.ex

75 lines
2.0 KiB
Elixir

defmodule Form do
@moduledoc """
A collection of loosely related functions helpful for filling out various forms at the city office.
"""
@type address_map :: %{
street: String.t(),
postal_code: String.t(),
city: String.t()
}
@type address_tuple :: {
street :: String.t(),
postal_code :: String.t(),
city :: String.t()
}
@type address :: address_map | address_tuple
@doc """
Generates a string of a given length.
This string can be used to fill out a form field that is supposed to have no value.
Such fields cannot be left empty because a malicious third party could fill them out with false data.
"""
@spec blanks(non_neg_integer()) :: String.t()
def blanks(n) do
String.duplicate("X", n)
end
@doc """
Splits the string into a list of uppercase letters.
This is needed for form fields that don't offer a single input for the whole string,
but instead require splitting the string into a predefined number of single-letter inputs.
"""
@spec letters(String.t()) :: [String.t()]
def letters(word) do
word
|> String.upcase()
|> String.split("", trim: true)
end
@doc """
Checks if the value has no more than the maximum allowed number of letters.
This is needed to check that the values of fields do not exceed the maximum allowed length.
It also tells you by how much the value exceeds the maximum.
"""
@spec check_length(String.t(), non_neg_integer()) :: :ok | {:error, pos_integer()}
def check_length(word, length) do
diff = String.length(word) - length
if diff <= 0 do
:ok
else
{:error, diff}
end
end
@doc """
Formats the address as an uppercase multiline string.
"""
@spec format_address(address) :: String.t()
def format_address(%{street: street, postal_code: postal_code, city: city}) do
format_address({street, postal_code, city})
end
def format_address({street, postal_code, city}) do
"""
#{String.upcase(street)}
#{String.upcase(postal_code)} #{String.upcase(city)}
"""
end
end