Skip to main content

payrail_core/
country.rs

1use crate::PaymentError;
2
3/// ISO 3166-1 alpha-2 country code.
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5pub struct CountryCode(String);
6
7impl CountryCode {
8    /// Parses and validates an ISO 3166-1 alpha-2 country code.
9    ///
10    /// # Errors
11    ///
12    /// Returns an error when the code is not exactly two ASCII letters.
13    pub fn new(code: impl AsRef<str>) -> Result<Self, PaymentError> {
14        let code = code.as_ref().trim();
15        if code.len() != 2 || !code.bytes().all(|byte| byte.is_ascii_alphabetic()) {
16            return Err(PaymentError::InvalidCountryCode(code.to_owned()));
17        }
18
19        Ok(Self(code.to_ascii_uppercase()))
20    }
21
22    /// Returns the normalized country code.
23    #[inline]
24    #[must_use]
25    pub fn as_str(&self) -> &str {
26        &self.0
27    }
28}
29
30impl AsRef<str> for CountryCode {
31    #[inline]
32    fn as_ref(&self) -> &str {
33        self.as_str()
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40
41    #[test]
42    fn new_uppercases_valid_code() {
43        let code = CountryCode::new("zm").expect("country should be valid");
44
45        assert_eq!(code.as_str(), "ZM");
46    }
47
48    #[test]
49    fn new_rejects_invalid_code() {
50        assert!(matches!(
51            CountryCode::new("ZMB"),
52            Err(PaymentError::InvalidCountryCode(_))
53        ));
54    }
55}