1use core::fmt;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10#[repr(u8)]
11pub enum State {
12 AC = 0,
13 AL = 1,
14 AM = 2,
15 AP = 3,
16 BA = 4,
17 CE = 5,
18 DF = 6,
19 ES = 7,
20 GO = 8,
21 MA = 9,
22 MG = 10,
23 MS = 11,
24 MT = 12,
25 PA = 13,
26 PB = 14,
27 PE = 15,
28 PI = 16,
29 PR = 17,
30 RJ = 18,
31 RN = 19,
32 RO = 20,
33 RR = 21,
34 RS = 22,
35 SC = 23,
36 SE = 24,
37 SP = 25,
38 TO = 26,
39}
40
41pub const ALL: [State; 27] = [
43 State::AC,
44 State::AL,
45 State::AM,
46 State::AP,
47 State::BA,
48 State::CE,
49 State::DF,
50 State::ES,
51 State::GO,
52 State::MA,
53 State::MG,
54 State::MS,
55 State::MT,
56 State::PA,
57 State::PB,
58 State::PE,
59 State::PI,
60 State::PR,
61 State::RJ,
62 State::RN,
63 State::RO,
64 State::RR,
65 State::RS,
66 State::SC,
67 State::SE,
68 State::SP,
69 State::TO,
70];
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74#[repr(u8)]
75pub enum Region {
76 Norte = 0,
77 Nordeste = 1,
78 CentroOeste = 2,
79 Sudeste = 3,
80 Sul = 4,
81}
82
83impl State {
84 pub fn abbreviation(self) -> &'static str {
86 match self {
87 State::AC => "AC",
88 State::AL => "AL",
89 State::AM => "AM",
90 State::AP => "AP",
91 State::BA => "BA",
92 State::CE => "CE",
93 State::DF => "DF",
94 State::ES => "ES",
95 State::GO => "GO",
96 State::MA => "MA",
97 State::MG => "MG",
98 State::MS => "MS",
99 State::MT => "MT",
100 State::PA => "PA",
101 State::PB => "PB",
102 State::PE => "PE",
103 State::PI => "PI",
104 State::PR => "PR",
105 State::RJ => "RJ",
106 State::RN => "RN",
107 State::RO => "RO",
108 State::RR => "RR",
109 State::RS => "RS",
110 State::SC => "SC",
111 State::SE => "SE",
112 State::SP => "SP",
113 State::TO => "TO",
114 }
115 }
116
117 pub fn name(self) -> &'static str {
119 match self {
120 State::AC => "Acre",
121 State::AL => "Alagoas",
122 State::AM => "Amazonas",
123 State::AP => "Amapá",
124 State::BA => "Bahia",
125 State::CE => "Ceará",
126 State::DF => "Distrito Federal",
127 State::ES => "Espírito Santo",
128 State::GO => "Goiás",
129 State::MA => "Maranhão",
130 State::MG => "Minas Gerais",
131 State::MS => "Mato Grosso do Sul",
132 State::MT => "Mato Grosso",
133 State::PA => "Pará",
134 State::PB => "Paraíba",
135 State::PE => "Pernambuco",
136 State::PI => "Piauí",
137 State::PR => "Paraná",
138 State::RJ => "Rio de Janeiro",
139 State::RN => "Rio Grande do Norte",
140 State::RO => "Rondônia",
141 State::RR => "Roraima",
142 State::RS => "Rio Grande do Sul",
143 State::SC => "Santa Catarina",
144 State::SE => "Sergipe",
145 State::SP => "São Paulo",
146 State::TO => "Tocantins",
147 }
148 }
149
150 pub fn region(self) -> Region {
152 match self {
153 State::AC | State::AM | State::AP | State::PA | State::RO | State::RR | State::TO => {
154 Region::Norte
155 }
156 State::AL
157 | State::BA
158 | State::CE
159 | State::MA
160 | State::PB
161 | State::PE
162 | State::PI
163 | State::RN
164 | State::SE => Region::Nordeste,
165 State::DF | State::GO | State::MS | State::MT => Region::CentroOeste,
166 State::ES | State::MG | State::RJ | State::SP => Region::Sudeste,
167 State::PR | State::RS | State::SC => Region::Sul,
168 }
169 }
170
171 pub fn from_abbreviation(s: &str) -> Option<Self> {
173 ALL.into_iter()
174 .find(|state| state.abbreviation().eq_ignore_ascii_case(s))
175 }
176}
177
178impl fmt::Display for State {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 f.write_str(self.abbreviation())
181 }
182}
183
184impl fmt::Display for Region {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.write_str(match self {
187 Region::Norte => "Norte",
188 Region::Nordeste => "Nordeste",
189 Region::CentroOeste => "Centro-Oeste",
190 Region::Sudeste => "Sudeste",
191 Region::Sul => "Sul",
192 })
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn all_states_count() {
202 assert_eq!(ALL.len(), 27);
203 }
204
205 #[test]
206 fn abbreviation_roundtrip() {
207 for state in ALL {
208 let abbr = state.abbreviation();
209 assert_eq!(abbr.len(), 2);
210 assert_eq!(State::from_abbreviation(abbr), Some(state));
211 }
212 }
213
214 #[test]
215 fn from_abbreviation_case_insensitive() {
216 for state in ALL {
217 let [first, second] = state.abbreviation().as_bytes() else {
218 unreachable!()
219 };
220 for abbreviation in [
221 [*first, *second],
222 [first.to_ascii_lowercase(), *second],
223 [*first, second.to_ascii_lowercase()],
224 [first.to_ascii_lowercase(), second.to_ascii_lowercase()],
225 ] {
226 let abbreviation = core::str::from_utf8(&abbreviation).unwrap();
227 assert_eq!(State::from_abbreviation(abbreviation), Some(state));
228 }
229 }
230 assert_eq!(State::from_abbreviation("XX"), None);
231 assert_eq!(State::from_abbreviation(""), None);
232 }
233
234 #[test]
235 fn display_shows_abbreviation() {
236 use alloc::string::ToString;
237 assert_eq!(State::SP.to_string(), "SP");
238 assert_eq!(State::RJ.to_string(), "RJ");
239 }
240
241 #[test]
242 fn region_classification() {
243 assert_eq!(State::SP.region(), Region::Sudeste);
244 assert_eq!(State::AM.region(), Region::Norte);
245 assert_eq!(State::BA.region(), Region::Nordeste);
246 assert_eq!(State::DF.region(), Region::CentroOeste);
247 assert_eq!(State::RS.region(), Region::Sul);
248 }
249
250 #[test]
251 fn name_not_empty() {
252 for state in ALL {
253 assert!(!state.name().is_empty());
254 }
255 }
256}