1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
|