diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 12:19:34 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 12:19:34 -0300 |
| commit | 075683ff44a254e99adbad8a0372ef80ff06151a (patch) | |
| tree | 648289c8485afc6f003a3f719fb937c21f149311 /lib/cepis/address.ex | |
| download | elixir-075683ff44a254e99adbad8a0372ef80ff06151a.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'lib/cepis/address.ex')
| -rw-r--r-- | lib/cepis/address.ex | 101 |
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 |