summaryrefslogtreecommitdiffstats
path: root/lib/cepis/address.ex
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cepis/address.ex')
-rw-r--r--lib/cepis/address.ex101
1 files changed, 101 insertions, 0 deletions
diff --git a/lib/cepis/address.ex b/lib/cepis/address.ex
new file mode 100644
index 0000000..0563912
--- /dev/null
+++ b/lib/cepis/address.ex
@@ -0,0 +1,101 @@
+defmodule Cepis.Address do
+ @moduledoc """
+ A CEP and everything the database knows about it.
+
+ **Field names are the API's own, in Portuguese** — `logradouro`, `bairro`,
+ `localidade`. Everything else in this library is English; the fields are the
+ one deliberate exception. The reason is fidelity: what you read in the JSON
+ at <https://cep.is/01001000> is exactly what you access on the struct.
+
+ Fields the database does not have for that CEP come back as empty strings,
+ never `nil` — that is what the API returns, and changing it here would create
+ a difference between reading the JSON and reading this library.
+
+ `geo` is the exception: it is `nil` when there is no coordinate. There the
+ difference is real, because the API **omits** the whole field in that case
+ (about 43% of CEPs) rather than sending null.
+
+ `raw` holds the decoded response exactly as it arrived, uninterpreted, so a
+ field the API grows before this library is updated is not lost.
+ """
+
+ alias Cepis.Geo
+
+ defstruct cep: "",
+ logradouro: "",
+ complemento: "",
+ unidade: "",
+ bairro: "",
+ localidade: "",
+ uf: "",
+ estado: "",
+ regiao: "",
+ ibge: "",
+ gia: "",
+ ddd: "",
+ siafi: "",
+ geo: nil,
+ raw: %{}
+
+ @type t :: %__MODULE__{
+ cep: String.t(),
+ logradouro: String.t(),
+ complemento: String.t(),
+ unidade: String.t(),
+ bairro: String.t(),
+ localidade: String.t(),
+ uf: String.t(),
+ estado: String.t(),
+ regiao: String.t(),
+ ibge: String.t(),
+ gia: String.t(),
+ ddd: String.t(),
+ siafi: String.t(),
+ geo: Geo.t() | nil,
+ raw: map()
+ }
+
+ @doc """
+ The CEP with its mask: `"01001-000"`.
+
+ The native contract always returns it unmasked; formatting is presentation,
+ and it lives here so it does not become a repeated interpolation in every
+ project that uses this. A CEP that is not 8 characters is returned unchanged.
+ """
+ @spec formatted_cep(t()) :: String.t()
+ def formatted_cep(%__MODULE__{cep: <<prefix::binary-size(5), suffix::binary-size(3)>>}) do
+ prefix <> "-" <> suffix
+ end
+
+ def formatted_cep(%__MODULE__{cep: cep}), do: cep
+
+ @doc false
+ @spec from_map(map()) :: t()
+ def from_map(data) when is_map(data) do
+ %__MODULE__{
+ cep: text(data, "cep"),
+ logradouro: text(data, "logradouro"),
+ complemento: text(data, "complemento"),
+ unidade: text(data, "unidade"),
+ bairro: text(data, "bairro"),
+ localidade: text(data, "localidade"),
+ uf: text(data, "uf"),
+ estado: text(data, "estado"),
+ regiao: text(data, "regiao"),
+ ibge: text(data, "ibge"),
+ gia: text(data, "gia"),
+ ddd: text(data, "ddd"),
+ siafi: text(data, "siafi"),
+ geo: Geo.from_map(Map.get(data, "geo")),
+ raw: data
+ }
+ end
+
+ defp text(data, key) do
+ case Map.get(data, key) do
+ value when is_binary(value) -> value
+ value when is_number(value) -> to_string(value)
+ _ -> ""
+ end
+ end
+end