iso3166_static/
lib.rs

1//! Static ISO 3166 Data
2
3#![doc = include_str!("../README.md")]
4#![no_std]
5
6#[cfg(feature = "serde")]
7mod serde_;
8
9use core::{
10    fmt::{Display, Formatter, Result as FmtResult},
11    str::FromStr,
12};
13
14iso3166_macros::generate_m49!();
15
16impl Numeric {
17    /// Create a new value the given string.
18    ///
19    /// This method will accept strings with 2-3 consecutive ASCII alphabetic characters left- or
20    /// right-padded by ASCII whitespace. The strings in question don't need to be in a particular
21    /// case. This is used by the [`FromStr`] implementation.
22    ///
23    /// # Errors
24    ///
25    /// - [`Error::InvalidCharset`] if the given slice does not contain ASCII characters.
26    /// - [`Error::InvalidLength`] if the given slice is not 2 or 3 characters long.
27    /// - [`Error::UnknownString`] if the given slice does not match any known Alpha2 code.
28    pub const fn from_str_slice(s: &str) -> Result<Self, Error> {
29        if !s.is_ascii() {
30            return Err(Error::InvalidCharset);
31        }
32
33        let s = s.trim_ascii();
34
35        let s_bytes = s.as_bytes();
36        let s_len = s_bytes.len();
37        if s_len != 2 && s_len != 3 {
38            return Err(Error::InvalidLength);
39        }
40
41        let mut src_bytes = [b' '; 3];
42
43        src_bytes[0] = s_bytes[0];
44        src_bytes[1] = s_bytes[1];
45        if s_len == 3 {
46            src_bytes[2] = s_bytes[2];
47        }
48        src_bytes.make_ascii_uppercase();
49
50        #[allow(unsafe_code)]
51        // SAFETY: this is safe becase we'e already established the source string is ASCII
52        let src = unsafe { str::from_utf8_unchecked(&src_bytes) };
53
54        if s_len == 2 {
55            Self::from_alpha2(src.trim_ascii())
56        } else {
57            Self::from_alpha3(src)
58        }
59    }
60}
61
62impl Display for Numeric {
63    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
64        write!(f, "{}", self.as_u16())
65    }
66}
67
68impl From<Numeric> for u16 {
69    fn from(value: Numeric) -> Self {
70        value.as_u16()
71    }
72}
73
74impl TryFrom<u16> for Numeric {
75    type Error = Error;
76
77    fn try_from(value: u16) -> Result<Self, Self::Error> {
78        Self::from_u16(value)
79    }
80}
81
82impl TryFrom<&str> for Numeric {
83    type Error = Error;
84
85    fn try_from(value: &str) -> Result<Self, Self::Error> {
86        <Self as FromStr>::from_str(value)
87    }
88}
89
90impl FromStr for Numeric {
91    type Err = Error;
92
93    fn from_str(s: &str) -> Result<Self, Self::Err> {
94        Self::from_str_slice(s)
95    }
96}
97
98impl From<Alpha2> for Numeric {
99    fn from(value: Alpha2) -> Self {
100        value.0
101    }
102}
103
104impl From<Alpha3> for Numeric {
105    fn from(value: Alpha3) -> Self {
106        value.0
107    }
108}
109
110impl PartialEq<Alpha2> for Numeric {
111    fn eq(&self, other: &Alpha2) -> bool {
112        self == &other.0
113    }
114}
115
116impl PartialEq<Alpha3> for Numeric {
117    fn eq(&self, other: &Alpha3) -> bool {
118        self == &other.0
119    }
120}
121
122/// A wrapper around the numeric enumeration requiring strings be Alpha-2 format.
123///
124/// When the `serde` feature is enabled, this type will be serialized as the Alpha-2 string.
125#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
126pub struct Alpha2(Numeric);
127
128impl Alpha2 {
129    /// Create a new value from an Alpha2 string.
130    ///
131    /// # Errors
132    ///
133    /// - [`Error::InvalidLength`] if the given slice is not 2 characters.
134    /// - [`Error::InvalidCharset`] if the given slice does not contain ASCII characters.
135    /// - [`Error::UnknownString`] if the given slice does not match any known Alpha2 code.
136    pub const fn from_alpha2(s: &str) -> Result<Self, Error> {
137        match Numeric::from_alpha2(s) {
138            Ok(value) => Ok(Self(value)),
139            Err(err) => Err(err),
140        }
141    }
142
143    /// Create a new value from an unsigned integer.
144    ///
145    /// # Errors
146    ///
147    /// - [`Error::UnknownCode`] if the given code value is not a numeric code.
148    pub const fn from_u16(value: u16) -> Result<Self, Error> {
149        match Numeric::from_u16(value) {
150            Ok(value) => Ok(Self(value)),
151            Err(err) => Err(err),
152        }
153    }
154
155    /// Create a new value from an Alpha3 code.
156    #[must_use]
157    pub const fn from_alpha3(value: Alpha3) -> Self {
158        Self(value.0)
159    }
160
161    /// Create a new value from a numeric code.
162    #[must_use]
163    pub const fn from_numeric(value: Numeric) -> Self {
164        Self(value)
165    }
166
167    /// Retrieve the static Alpha2 code string for this value.
168    #[must_use]
169    pub const fn as_str(&self) -> &'static str {
170        self.0.as_alpha2_str()
171    }
172}
173
174impl Display for Alpha2 {
175    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
176        f.write_str(self.as_str())
177    }
178}
179
180impl FromStr for Alpha2 {
181    type Err = Error;
182
183    fn from_str(value: &str) -> Result<Self, Self::Err> {
184        Self::from_alpha2(value)
185    }
186}
187
188impl TryFrom<&str> for Alpha2 {
189    type Error = Error;
190
191    fn try_from(value: &str) -> Result<Self, Self::Error> {
192        Self::from_alpha2(value)
193    }
194}
195
196impl TryFrom<u16> for Alpha2 {
197    type Error = Error;
198
199    fn try_from(value: u16) -> Result<Self, Self::Error> {
200        Self::from_u16(value)
201    }
202}
203
204impl From<Alpha3> for Alpha2 {
205    fn from(value: Alpha3) -> Self {
206        Self::from_alpha3(value)
207    }
208}
209
210impl From<Numeric> for Alpha2 {
211    fn from(value: Numeric) -> Self {
212        Self::from_numeric(value)
213    }
214}
215
216impl PartialEq<Alpha3> for Alpha2 {
217    fn eq(&self, other: &Alpha3) -> bool {
218        self.0 == other.0
219    }
220}
221
222impl PartialEq<Numeric> for Alpha2 {
223    fn eq(&self, other: &Numeric) -> bool {
224        self.0 == *other
225    }
226}
227
228/// A wrapper around the numeric enumeration requiring strings be Alpha-2 format.
229///
230/// When the `serde` feature is enabled, this type will be serialized as the Alpha-3 string.
231///
232/// # Examples
233///
234/// ```rust
235/// use iso3166_static::Alpha3;
236/// const ALPHA3_USA: &str = "USA";
237///
238/// let alpha3 = Alpha3::from_alpha3(ALPHA3_USA).expect("valid alpha3");
239///
240/// assert_eq!(ALPHA3_USA, alpha3.as_str());
241/// ```
242#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
243pub struct Alpha3(Numeric);
244
245impl Alpha3 {
246    /// Create a new value from an Alpha2 string.
247    ///
248    /// This method strictly validates the given slice and only accepts valid upper-case Alpha3
249    /// codes with no padding.
250    ///
251    /// # Errors
252    ///
253    /// - [`Error::InvalidLength`] if the given slice is not 3 characters.
254    /// - [`Error::InvalidCharset`] if the given slice contains non-ASCII-7 characters.
255    /// - [`Error::UnknownString`] if the given slice does not match any known Alpha3 code.
256    pub const fn from_alpha3(s: &str) -> Result<Self, Error> {
257        match Numeric::from_alpha3(s) {
258            Ok(value) => Ok(Self(value)),
259            Err(err) => Err(err),
260        }
261    }
262
263    /// Create a new value from an unsigned integer.
264    ///
265    /// # Errors
266    ///
267    /// - [`Error::UnknownCode`] if the given code value is not a numeric code.
268    pub const fn from_u16(value: u16) -> Result<Self, Error> {
269        match Numeric::from_u16(value) {
270            Ok(value) => Ok(Self(value)),
271            Err(err) => Err(err),
272        }
273    }
274
275    /// Create a new value from an Alpha2 code.
276    #[must_use]
277    pub const fn from_alpha2(value: Alpha2) -> Self {
278        Self(value.0)
279    }
280
281    /// Create a new value from a numeric code.
282    #[must_use]
283    pub const fn from_numeric(value: Numeric) -> Self {
284        Self(value)
285    }
286
287    /// Retrieve the static Alpha2 code string for this value.
288    #[must_use]
289    pub const fn as_str(&self) -> &'static str {
290        self.0.as_alpha3_str()
291    }
292}
293
294impl Display for Alpha3 {
295    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
296        f.write_str(self.as_str())
297    }
298}
299
300impl FromStr for Alpha3 {
301    type Err = Error;
302
303    fn from_str(value: &str) -> Result<Self, Self::Err> {
304        Self::from_alpha3(value)
305    }
306}
307
308impl TryFrom<&str> for Alpha3 {
309    type Error = Error;
310
311    fn try_from(value: &str) -> Result<Self, Self::Error> {
312        Self::from_alpha3(value)
313    }
314}
315
316impl TryFrom<u16> for Alpha3 {
317    type Error = Error;
318
319    fn try_from(value: u16) -> Result<Self, Self::Error> {
320        Self::from_u16(value)
321    }
322}
323
324impl From<Alpha2> for Alpha3 {
325    fn from(value: Alpha2) -> Self {
326        Self::from_alpha2(value)
327    }
328}
329
330impl From<Numeric> for Alpha3 {
331    fn from(value: Numeric) -> Self {
332        Self::from_numeric(value)
333    }
334}
335
336impl PartialEq<Alpha2> for Alpha3 {
337    fn eq(&self, other: &Alpha2) -> bool {
338        self.0 == other.0
339    }
340}
341
342impl PartialEq<Numeric> for Alpha3 {
343    fn eq(&self, other: &Numeric) -> bool {
344        self.0 == *other
345    }
346}
347
348#[cfg(test)]
349mod test {
350    extern crate std;
351
352    use super::*;
353    use std::string::ToString;
354
355    const USA_EXPECTED2: &str = "US";
356    const USA_EXPECTED3: &str = "USA";
357    const USA_EXPECTED_U16: u16 = 840;
358
359    #[test]
360    fn numeric_display() {
361        let src = Numeric::UnitedStatesOfAmerica;
362
363        assert_eq!("840", src.to_string());
364    }
365
366    #[test]
367    fn numeric_u16_roundtrip() {
368        let actual = Numeric::try_from(USA_EXPECTED_U16).expect("valid u16");
369        assert_eq!(USA_EXPECTED_U16, u16::from(actual));
370    }
371
372    #[yare::parameterized(
373        pass = {USA_EXPECTED_U16, Ok(Numeric::UnitedStatesOfAmerica)},
374        unknown = {u16::MAX, Err(Error::UnknownCode)},
375        unknown2 = {123, Err(Error::UnknownCode)},
376    )]
377    fn numeric_from_u16(input: u16, expected: Result<Numeric, Error>) {
378        let actual = Numeric::try_from(input);
379        assert_eq!(expected, actual);
380    }
381
382    #[yare::parameterized(
383        pass = {USA_EXPECTED2, Ok(Numeric::UnitedStatesOfAmerica)},
384        trim_both = {"  US  ", Err(Error::InvalidLength)},
385        unknown = {"XX", Err(Error::UnknownString)},
386    )]
387    fn numeric_from_alpha2(input: &str, expected: Result<Numeric, Error>) {
388        let actual = Numeric::from_alpha2(input);
389        assert_eq!(expected, actual);
390    }
391
392    #[yare::parameterized(
393        pass = {USA_EXPECTED3, Ok(Numeric::UnitedStatesOfAmerica)},
394        trim_both = {"  USA  ", Err(Error::InvalidLength)},
395        unknown = {"XXX", Err(Error::UnknownString)},
396    )]
397    fn numeric_from_alpha3(input: &str, expected: Result<Numeric, Error>) {
398        let actual = Numeric::from_alpha3(input);
399        assert_eq!(expected, actual);
400    }
401
402    /// This test exercises the `FromStr` implementation of `Numeric`, which in turn exercises
403    /// [`Numeric::from_str_slice`].
404    #[yare::parameterized(
405        pass = {USA_EXPECTED3, Ok(Numeric::UnitedStatesOfAmerica)},
406        trim_both2 = {"  US   ", Ok(Numeric::UnitedStatesOfAmerica)},
407        trim_start2 = {"     US", Ok(Numeric::UnitedStatesOfAmerica)},
408        trim_end2 = {"US    ", Ok(Numeric::UnitedStatesOfAmerica)},
409        trim_both3 = {"  USA  ", Ok(Numeric::UnitedStatesOfAmerica)},
410        trim_start3 = {"      USA", Ok(Numeric::UnitedStatesOfAmerica)},
411        trim_end3 = {"USA     ", Ok(Numeric::UnitedStatesOfAmerica)},
412        unknown = {"XXX", Err(Error::UnknownString)},
413        poop = {"💩💩💩", Err(Error::InvalidCharset)},
414        length = {"XXXX", Err(Error::InvalidLength)},
415    )]
416    fn numeric_try_from_str(input: &str, expected: Result<Numeric, Error>) {
417        let actual = Numeric::try_from(input);
418        assert_eq!(expected, actual);
419    }
420
421    #[yare::parameterized(
422        pass = {USA_EXPECTED2, Ok(Alpha2::from_numeric(Numeric::UnitedStatesOfAmerica))},
423    )]
424    fn alpha2_from_str(input: &str, expected: Result<Alpha2, Error>) {
425        let actual = Alpha2::from_str(input);
426        assert_eq!(expected, actual);
427    }
428
429    #[yare::parameterized(
430        pass = {USA_EXPECTED3, Ok(Alpha3::from_numeric(Numeric::UnitedStatesOfAmerica))},
431    )]
432    fn alpha3_from_str(input: &str, expected: Result<Alpha3, Error>) {
433        let actual = Alpha3::from_str(input);
434        assert_eq!(expected, actual);
435    }
436}