country_code/iso3166_2/
mod.rs1#[macro_export]
5macro_rules! country_subdivision_code {
6 (
7 $country_code_ty:ty, $country_code_val:expr;
8
9 $( #[$meta:meta] )*
10 $pub:vis enum $name:ident {
11 $(
12 $( #[$variant_meta:meta] )*
13 $variant:ident,
14 )+
15 }
16 ) => {
17 $(#[$meta])*
18 $pub enum $name {
19 $(
20 $( #[$variant_meta] )*
21 $variant,
22 )+
23 Other($crate::alloc::boxed::Box<str>),
24 }
25
26 impl $name {
28 pub const COUNTRY_CODE: $country_code_ty = $country_code_val;
29
30 pub const VARS: &'static [$name] = &[
31 $(
32 $name::$variant,
33 )+
34 ];
35 }
36
37 impl ::core::str::FromStr for $name {
39 type Err = $crate::error::CountrySubdivisionCodeParseError;
40
41 fn from_str(s: &str) -> Result<Self, Self::Err> {
42 let country_code_s = s.chars().take_while(|x| x != &'-' && x != &'_')
43 .collect::<$crate::alloc::string::String>();
44 let country_code = country_code_s.parse::<$country_code_ty>()
45 .map_err(|_| $crate::error::CountrySubdivisionCodeParseError::CountryCodeInvalid(country_code_s.as_str().into()))?;
46
47 if country_code != Self::COUNTRY_CODE {
48 return Err($crate::error::CountrySubdivisionCodeParseError::CountryCodeMismatch(country_code_s.into()));
49 }
50
51 let subdivision_code_s = if s.len() > country_code_s.len() + 1 {
52 &s[country_code_s.len() + 1..]
53 } else {
54 return Err($crate::error::CountrySubdivisionCodeParseError::SubdivisionCodeMissing);
55 };
56
57 match subdivision_code_s {
58 $(
59 ::core::stringify!($variant) => Ok(Self::$variant),
60 )+
61 s if s.len() == 2 => Ok(Self::Other(s.into())),
62 s => Err($crate::error::CountrySubdivisionCodeParseError::SubdivisionCodeInvalid(s.into()))
63 }
64 }
65 }
66
67 impl ::core::fmt::Display for $name {
69 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
70 match self {
71 $(
72 Self::$variant => ::core::write!(f, "{}-{}", $name::COUNTRY_CODE, ::core::stringify!($variant)),
73 )+
74 Self::Other(s) => ::core::write!(f, "{}-{}", $name::COUNTRY_CODE, s)
75 }
76 }
77 }
78
79 impl ::core::default::Default for $name {
81 fn default() -> Self {
82 Self::Other(Default::default())
83 }
84 }
85
86 impl ::core::cmp::PartialEq for $name {
88 fn eq(&self, other: &Self) -> bool {
89 $crate::alloc::format!("{}", self) == $crate::alloc::format!("{}", other)
90 }
91 }
92
93 impl ::core::cmp::Eq for $name {
94 }
95
96 $crate::impl_partial_eq_str_for_display! { str, $name }
98 $crate::impl_partial_eq_str_for_display! { &'a str, $name }
99 $crate::impl_partial_eq_str_for_display! { $crate::alloc::borrow::Cow<'a, str>, $name }
100 $crate::impl_partial_eq_str_for_display! { $crate::alloc::string::String, $name }
101
102 #[cfg(feature = "std")]
104 impl ::std::hash::Hash for $name {
105 fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
106 $crate::alloc::format!("{}", self).hash(state);
107 }
108 }
109
110 #[cfg(feature = "serde")]
112 impl<'de> ::serde::Deserialize<'de> for $name {
113 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
114 where
115 D: ::serde::Deserializer<'de>,
116 {
117 use ::core::str::FromStr as _;
118
119 let s = $crate::alloc::boxed::Box::<str>::deserialize(deserializer)?;
120 Self::from_str(&s).map_err(::serde::de::Error::custom)
121 }
122 }
123
124 #[cfg(feature = "serde")]
126 impl ::serde::Serialize for $name {
127 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
128 where
129 S: ::serde::Serializer,
130 {
131 use $crate::alloc::string::ToString as _;
132
133 self.to_string().serialize(serializer)
134 }
135 }
136 };
137}
138
139pub mod cn;
141pub mod us;
142
143pub use cn::CountrySubdivisionCode as CNSubdivisionCode;
144pub use us::CountrySubdivisionCode as USSubdivisionCode;
145
146use crate::iso3166_1::alpha_2::CountryCode;
150
151#[derive(Debug, Clone, PartialEq, Eq)]
152#[cfg_attr(feature = "std", derive(Hash))]
153pub enum SubdivisionCode {
154 CN(CNSubdivisionCode),
155 US(USSubdivisionCode),
156 Other(CountryCode, Option<::alloc::boxed::Box<str>>),
157}
158
159impl ::core::str::FromStr for SubdivisionCode {
161 type Err = crate::error::CountrySubdivisionCodeParseError;
162
163 fn from_str(s: &str) -> Result<Self, Self::Err> {
164 let country_code_s = s
165 .chars()
166 .take_while(|x| x != &'-' && x != &'_')
167 .collect::<::alloc::string::String>();
168 let country_code = country_code_s.parse::<CountryCode>().map_err(|_| {
169 crate::error::CountrySubdivisionCodeParseError::CountryCodeInvalid(
170 country_code_s.as_str().into(),
171 )
172 })?;
173
174 match country_code {
175 CountryCode::CN => {
176 let subdivision = s.parse::<CNSubdivisionCode>()?;
177 Ok(Self::CN(subdivision))
178 }
179 CountryCode::US => {
180 let subdivision = s.parse::<USSubdivisionCode>()?;
181 Ok(Self::US(subdivision))
182 }
183 country => {
184 let subdivision_code_s = if s.len() > country_code_s.len() + 1 {
185 let subdivision_code_s = &s[country_code_s.len() + 1..];
186 Some(subdivision_code_s.into())
187 } else {
188 None
189 };
190
191 Ok(Self::Other(country, subdivision_code_s))
192 }
193 }
194 }
195}
196
197impl ::core::fmt::Display for SubdivisionCode {
199 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
200 match self {
201 Self::CN(subdivision) => ::core::write!(f, "{subdivision}"),
202 Self::US(subdivision) => ::core::write!(f, "{subdivision}"),
203 Self::Other(country, Some(s)) => ::core::write!(f, "{country}-{s}"),
204 Self::Other(country, None) => ::core::write!(f, "{country}-"),
205 }
206 }
207}
208
209crate::impl_partial_eq_str_for_display! { str, SubdivisionCode }
211crate::impl_partial_eq_str_for_display! { &'a str, SubdivisionCode }
212crate::impl_partial_eq_str_for_display! { ::alloc::borrow::Cow<'a, str>, SubdivisionCode }
213crate::impl_partial_eq_str_for_display! { ::alloc::string::String, SubdivisionCode }
214
215#[cfg(feature = "serde")]
217impl<'de> ::serde::Deserialize<'de> for SubdivisionCode {
218 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
219 where
220 D: ::serde::Deserializer<'de>,
221 {
222 use ::core::str::FromStr as _;
223
224 let s = ::alloc::boxed::Box::<str>::deserialize(deserializer)?;
225 Self::from_str(&s).map_err(::serde::de::Error::custom)
226 }
227}
228
229#[cfg(feature = "serde")]
231impl ::serde::Serialize for SubdivisionCode {
232 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
233 where
234 S: ::serde::Serializer,
235 {
236 use ::alloc::string::ToString as _;
237
238 self.to_string().serialize(serializer)
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 use alloc::string::ToString as _;
247
248 #[test]
249 fn test_subdivision_code() {
250 assert_eq!(
252 SubdivisionCode::US(us::CountrySubdivisionCode::NY).to_string(),
253 "US-NY"
254 );
255 assert_eq!(
256 "US-NY".parse::<SubdivisionCode>().unwrap(),
257 SubdivisionCode::US(us::CountrySubdivisionCode::NY)
258 );
259
260 assert_eq!(
262 SubdivisionCode::Other(CountryCode::ZW, Some("BU".into())).to_string(),
263 "ZW-BU"
264 );
265 assert_eq!(
266 "ZW-BU".parse::<SubdivisionCode>().unwrap(),
267 SubdivisionCode::Other(CountryCode::ZW, Some("BU".into()))
268 );
269
270 assert_eq!(
272 SubdivisionCode::Other(CountryCode::AI, None).to_string(),
273 "AI-"
274 );
275 assert_eq!(
276 "AI-".parse::<SubdivisionCode>().unwrap(),
277 SubdivisionCode::Other(CountryCode::AI, None)
278 );
279 }
280}