1"""Country."""
2
3# local
4from validators.utils import validator
5
6# fmt: off
7_alpha3_to_alpha2 = {
8 # A
9 "ABW": "AW", "AFG": "AF", "AGO": "AO", "AIA": "AI", "ALB": "AL", "AND": "AD", "ANT": "AN",
10 "ARE": "AE", "ARG": "AR", "ARM": "AM", "ASM": "AS", "ATA": "AQ", "ATF": "TF", "ATG": "AG",
11 "AUS": "AU", "AUT": "AT", "AZE": "AZ",
12 # B
13 "BDI": "BI", "BEL": "BE", "BEN": "BJ", "BFA": "BF", "BGD": "BD", "BGR": "BG", "BHR": "BH",
14 "BHS": "BS", "BIH": "BA", "BLR": "BY", "BLZ": "BZ", "BMU": "BM", "BOL": "BO", "BRA": "BR",
15 "BRB": "BB", "BRN": "BN", "BTN": "BT", "BVT": "BV", "BWA": "BW",
16 # C
17 "CAF": "CF", "CAN": "CA", "CCK": "CC", "CHE": "CH", "CHL": "CL", "CHN": "CN", "CMR": "CM",
18 "COD": "CD", "COG": "CG", "COK": "CK", "COL": "CO", "COM": "KM", "CPV": "CV", "CRI": "CR",
19 "CUB": "CU", "CXR": "CX", "CYM": "KY", "CYP": "CY", "CZE": "CZ",
20 # D
21 "DEU": "DE", "DJI": "DJ", "DMA": "DM", "DNK": "DK", "DOM": "DO", "DZA": "DZ",
22 # E
23 "ECU": "EC", "EGY": "EG", "ERI": "ER", "ESH": "EH", "ESP": "ES", "EST": "EE", "ETH": "ET",
24 # F
25 "FIN": "FI", "FJI": "FJ", "FLK": "FK", "FRA": "FR", "FRO": "FO", "FSM": "FM",
26 # G
27 "GAB": "GA", "GBR": "GB", "GEO": "GE", "GGY": "GG", "GHA": "GH", "GIB": "GI", "GIN": "GN",
28 "GLP": "GP", "GMB": "GM", "GNB": "GW", "GNQ": "GQ", "GRC": "GR", "GRD": "GD", "GRL": "GL",
29 "GTM": "GT", "GUF": "GF", "GUM": "GU", "GUY": "GY",
30 # H
31 "HKG": "HK", "HMD": "HM", "HND": "HN", "HRV": "HR", "HTI": "HT", "HUN": "HU",
32 # I
33 "IDN": "ID", "IMN": "IM", "IND": "IN", "IOT": "IO", "IRL": "IE", "IRN": "IR", "IRQ": "IQ",
34 "ISL": "IS", "ISR": "IL", "ITA": "IT",
35 # J
36 "JAM": "JM", "JEY": "JE", "JOR": "JO", "JPN": "JP",
37 # K
38 "KAZ": "KZ", "KEN": "KE", "KGZ": "KG", "KHM": "KH", "KIR": "KI", "KNA": "KN", "KOR": "KR",
39 "KWT": "KW",
40 # L
41 "LAO": "LA", "LBN": "LB", "LBR": "LR", "LBY": "LY", "LCA": "LC", "LIE": "LI", "LKA": "LK",
42 "LSO": "LS", "LTU": "LT", "LUX": "LU", "LVA": "LV",
43 # M
44 "MAC": "MO", "MAR": "MA", "MCO": "MC", "MDA": "MD", "MDG": "MG", "MDV": "MV", "MEX": "MX",
45 "MHL": "MH", "MKD": "MK", "MLI": "ML", "MLT": "MT", "MMR": "MM", "MNE": "ME", "MNG": "MN",
46 "MNP": "MP", "MOZ": "MZ", "MRT": "MR", "MSR": "MS", "MTQ": "MQ", "MUS": "MU", "MWI": "MW",
47 "MYS": "MY", "MYT": "YT",
48 # N
49 "NAM": "NA", "NCL": "NC", "NER": "NE", "NFK": "NF", "NGA": "NG", "NIC": "NI", "NIU": "NU",
50 "NLD": "NL", "NOR": "NO", "NPL": "NP", "NRU": "NR", "NZL": "NZ",
51 # O
52 "OMN": "OM",
53 # P
54 "PAK": "PK", "PAN": "PA", "PCN": "PN", "PER": "PE", "PHL": "PH", "PLW": "PW", "PNG": "PG",
55 "POL": "PL", "PRI": "PR", "PRK": "KP", "PRT": "PT", "PRY": "PY", "PSE": "PS", "PYF": "PF",
56 # Q
57 "QAT": "QA",
58 # R
59 "REU": "RE", "ROU": "RO", "RUS": "RU", "RWA": "RW",
60 # S
61 "SAU": "SA", "SDN": "SD", "SEN": "SN", "SGP": "SG", "SGS": "GS", "SHN": "SH", "SJM": "SJ",
62 "SLB": "SB", "SLE": "SL", "SLV": "SV", "SMR": "SM", "SOM": "SO", "SPM": "PM", "SRB": "RS",
63 "STP": "ST", "SUR": "SR", "SVK": "SK", "SVN": "SI", "SWE": "SE", "SWZ": "SZ", "SYC": "SC",
64 "SYR": "SY",
65 # T
66 "TCA": "TC", "TCD": "TD", "TGO": "TG", "THA": "TH", "TJK": "TJ", "TKL": "TK", "TKM": "TM",
67 "TLS": "TL", "TON": "TO", "TTO": "TT", "TUN": "TN", "TUR": "TR", "TUV": "TV", "TWN": "TW",
68 "TZA": "TZ",
69 # U
70 "UGA": "UG", "UKR": "UA", "UMI": "UM", "URY": "UY", "USA": "US", "UZB": "UZ",
71 # V
72 "VAT": "VA", "VCT": "VC", "VEN": "VE", "VGB": "VG", "VIR": "VI", "VNM": "VN", "VUT": "VU",
73 # W
74 "WLF": "WF", "WSM": "WS",
75 # Y
76 "YEM": "YE",
77 # Z
78 "ZAF": "ZA", "ZMB": "ZM", "ZWE": "ZW",
79}
80_calling_codes = {
81 # A
82 "ABW": "+297", "AFG": "+93", "AGO": "+244", "AIA": "+1-264", "ALB": "+355", "AND": "+376",
83 "ANT": "+599", "ARE": "+971", "ARG": "+54", "ARM": "+374", "ASM": "+1-684", "ATA": "+672",
84 "ATG": "+1-268", "AUS": "+61", "AUT": "+43", "AZE": "+994",
85 # B
86 "BDI": "+257", "BEL": "+32", "BEN": "+229", "BFA": "+226", "BGD": "+880", "BGR": "+359",
87 "BHR": "+973", "BHS": "+1-242", "BIH": "+387", "BLR": "+375", "BLZ": "+501",
88 "BMU": "+1-441", "BOL": "+591", "BRA": "+55", "BRB": "+1-246", "BRN": "+673", "BTN": "+975",
89 "BWA": "+267",
90 # C
91 "CAF": "+236", "CAN": "+1", "CCK": "+61", "CHE": "+41", "CHL": "+56", "CHN": "+86",
92 "CMR": "+237", "COD": "+243", "COG": "+242", "COK": "+682", "COL": "+57", "COM": "+269",
93 "CPV": "+238", "CRI": "+506", "CUB": "+53", "CXR": "+61", "CYM": "+1-345", "CYP": "+357",
94 "CZE": "+420",
95 # D
96 "DEU": "+49", "DJI": "+253", "DMA": "+1-767", "DNK": "+45", "DOM": "+1-809", "DZA": "+213",
97 # E
98 "ECU": "+593", "EGY": "+20", "ERI": "+291", "ESH": "+212", "ESP": "+34", "EST": "+372",
99 "ETH": "+251",
100 # F
101 "FIN": "+358", "FJI": "+679", "FLK": "+500", "FRA": "+33", "FRO": "+298", "FSM": "+691",
102 # G
103 "GAB": "+241", "GBR": "+44", "GEO": "+995", "GGY": "+44-1481", "GHA": "+233", "GIB": "+350",
104 "GIN": "+224", "GLP": "+590", "GMB": "+220", "GNB": "+245", "GNQ": "+240", "GRC": "+30",
105 "GRD": "+1-473", "GRL": "+299", "GTM": "+502", "GUF": "+594", "GUM": "+1-671",
106 "GUY": "+592",
107 # H
108 "HKG": "+852", "HMD": "+672", "HND": "+504", "HRV": "+385", "HTI": "+509", "HUN": "+36",
109 # I
110 "IDN": "+62", "IMN": "+44-1624", "IND": "+91", "IOT": "+246", "IRL": "+353", "IRN": "+98",
111 "IRQ": "+964", "ISL": "+354", "ISR": "+972", "ITA": "+39",
112 # J
113 "JAM": "+1-876", "JEY": "+44-1534", "JOR": "+962", "JPN": "+81",
114 # K
115 "KAZ": "+7", "KEN": "+254", "KGZ": "+996", "KHM": "+855", "KIR": "+686", "KNA": "+1-869",
116 "KOR": "+82", "KWT": "+965",
117 # L
118 "LAO": "+856", "LBN": "+961", "LBR": "+231", "LBY": "+218", "LCA": "+1-758", "LIE": "+423",
119 "LKA": "+94", "LSO": "+266", "LTU": "+370", "LUX": "+352", "LVA": "+371",
120 # M
121 "MAC": "+853", "MAR": "+212", "MCO": "+377", "MDA": "+373", "MDG": "+261", "MDV": "+960",
122 "MEX": "+52", "MHL": "+692", "MKD": "+389", "MLI": "+223", "MLT": "+356", "MMR": "+95",
123 "MNE": "+382", "MNG": "+976", "MNP": "+1-670", "MOZ": "+258", "MRT": "+222",
124 "MSR": "+1-664", "MTQ": "+596", "MUS": "+230", "MWI": "+265", "MYS": "+60", "MYT": "+262",
125 # N
126 "NAM": "+264", "NCL": "+687", "NER": "+227", "NFK": "+672", "NGA": "+234", "NIC": "+505",
127 "NIU": "+683", "NLD": "+31", "NOR": "+47", "NPL": "+977", "NRU": "+674", "NZL": "+64",
128 # O
129 "OMN": "+968",
130 # P
131 "PAK": "+92", "PAN": "+507", "PCN": "+64", "PER": "+51", "PHL": "+63", "PLW": "+680",
132 "PNG": "+675", "POL": "+48", "PRI": "+1-787", "PRK": "+850", "PRT": "+351", "PRY": "+595",
133 "PSE": "+970", "PYF": "+689",
134 # Q
135 "QAT": "+974",
136 # R
137 "REU": "+262", "ROU": "+40", "RUS": "+7", "RWA": "+250",
138 # S
139 "SAU": "+966", "SDN": "+249", "SEN": "+221", "SGP": "+65", "SHN": "+290", "SJM": "+47",
140 "SLB": "+677", "SLE": "+232", "SLV": "+503", "SMR": "+378", "SOM": "+252", "SPM": "+508",
141 "SRB": "+381", "STP": "+239", "SUR": "+597", "SVK": "+421", "SVN": "+386", "SWE": "+46",
142 "SWZ": "+268", "SYC": "+248", "SYR": "+963",
143 # T
144 "TCA": "+1-649", "TCD": "+235", "TGO": "+228", "THA": "+66", "TJK": "+992", "TKL": "+690",
145 "TKM": "+993", "TLS": "+670", "TON": "+676", "TTO": "+1-868", "TUN": "+216", "TUR": "+90",
146 "TUV": "+688", "TWN": "+886", "TZA": "+255",
147 # U
148 "UGA": "+256", "UKR": "+380", "UMI": "+1", "URY": "+598", "USA": "+1", "UZB": "+998",
149 # V
150 "VAT": "+379", "VCT": "+1-784", "VEN": "+58", "VGB": "+1-284", "VIR": "+1-340",
151 "VNM": "+84", "VUT": "+678",
152 # W
153 "WLF": "+681", "WSM": "+685",
154 # Y
155 "YEM": "+967",
156 # Z
157 "ZAF": "+27", "ZMB": "+260", "ZWE": "+263"
158}
159_numeric = {
160 "004", "008", "010", "012", "016", "020", "024", "028", "031", "032",
161 "036", "040", "044", "048", "050", "051", "052", "056", "060", "064",
162 "068", "070", "072", "074", "076", "084", "086", "090", "092", "096",
163 "100", "104", "108", "112", "116", "120", "124", "132", "136", "140",
164 "144", "148", "152", "156", "158", "162", "166", "170", "174", "175",
165 "178", "180", "184", "188", "191", "192", "196", "203", "204", "208",
166 "212", "214", "218", "222", "226", "231", "232", "233", "234", "238",
167 "239", "242", "246", "248", "250", "254", "258", "260", "262", "266",
168 "268", "270", "275", "276", "288", "292", "296", "300", "304", "308",
169 "312", "316", "320", "324", "328", "332", "334", "340", "344", "348",
170 "352", "356", "360", "364", "368", "372", "376", "380", "384", "388",
171 "392", "398", "400", "404", "408", "410", "414", "417", "418", "422",
172 "426", "428", "430", "434", "438", "440", "442", "446", "450", "454",
173 "458", "462", "466", "470", "474", "478", "480", "484", "492", "496",
174 "498", "499", "500", "504", "508", "512", "516", "520", "524", "528",
175 "531", "533", "534", "535", "540", "548", "554", "558", "562", "566",
176 "570", "574", "578", "580", "581", "583", "584", "585", "586", "591",
177 "598", "600", "604", "608", "612", "616", "620", "624", "626", "630",
178 "634", "638", "642", "643", "646", "652", "654", "659", "660", "662",
179 "663", "666", "670", "674", "678", "682", "686", "688", "690", "694",
180 "702", "703", "704", "705", "706", "710", "716", "724", "728", "729",
181 "732", "740", "744", "748", "752", "756", "760", "762", "764", "768",
182 "772", "776", "780", "784", "788", "792", "795", "796", "798", "800",
183 "804", "807", "818", "826", "831", "832", "833", "834", "840", "850",
184 "854", "858", "860", "862", "876", "882", "887", "894",
185}
186_currency_iso4217 = {
187 # https://en.wikipedia.org/wiki/ISO_4217
188 "AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
189 "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN",
190 "BWP", "BYN", "BZD",
191 "CAD", "CDF", "CHE", "CHF", "CHW", "CKD", "CLF", "CLP", "CNY", "COP", "CRC", "CUC", "CUP",
192 "CVE", "CZK",
193 "DJF", "DKK", "DOP", "DZD",
194 "EGP", "ERN", "ETB", "EUR",
195 "FJD", "FKP",
196 "GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
197 "HKD", "HNL", "HRK", "HTG", "HUF",
198 "IDR", "IEP", "ILS", "INR", "IQD", "IRR", "ISK",
199 "JMD", "JOD", "JPY",
200 "KES", "KGS", "KHR", "KID", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
201 "LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
202 "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRU", "MUR", "MVR",
203 "MWK", "MXN", "MYR", "MZN",
204 "NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
205 "OMR",
206 "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
207 "QAR",
208 "RON", "RSD", "RUB", "RWF",
209 "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS",
210 "SRD", "SSP", "STN", "SVC", "SYP", "SZL",
211 "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
212 "UAH", "UGX", "USD", "UYU", "UZS",
213 "VED", "VES", "VND", "VUV",
214 "WST",
215 "XAF", "XCD", "XDR", "XOF", "XPF",
216 "YER",
217 "ZAR", "ZMW", "ZWL"
218}
219_currency_symbols = {
220 # https://en.wikipedia.org/wiki/Currency_sign_(generic)
221 "؋", "฿", "₵", "₡", "¢", "$", "₫", "֏", "€", "ƒ", "₣", "₲", "₴", "₭", "₾", "£", "₺", "₼", "₦",
222 "₱", "元", "圆", "圓", "﷼", "៛", "₽", "₹", "रू", "රු", "૱", "௹", "꠸", "Rs", "₪", "⃀" "৳", "₸",
223 "₮", "₩", "¥", "円", "₿", "¤"
224}
225# fmt: on
226
227
228def _get_code_type(format_type: str):
229 """Returns the type of country code."""
230 if format_type.isdecimal():
231 return "numeric"
232 if format_type.isalpha():
233 if len(format_type) == 2:
234 return "alpha2"
235 if len(format_type) == 3:
236 return "alpha3"
237 return "invalid"
238
239
240@validator
241def calling_code(value: str, /):
242 """Validates given calling code.
243
244 This performs country's calling code validation.
245
246 Examples:
247 >>> calling_code('+91')
248 True
249 >>> calling_code('-31')
250 ValidationError(func=calling_code, args={'value': '-31'})
251
252 Args:
253 value:
254 Country's calling code string to validate.
255
256 Returns:
257 (Literal[True]): If `value` is a valid calling code.
258 (ValidationError): If `value` is an invalid calling code.
259 """
260 if not value:
261 return False
262
263 return value in set(_calling_codes.values())
264
265
266@validator
267def country_code(value: str, /, *, iso_format: str = "auto", ignore_case: bool = False):
268 """Validates given country code.
269
270 This performs a case-sensitive [ISO 3166][1] country code validation.
271
272 [1]: https://www.iso.org/iso-3166-country-codes.html
273
274 Examples:
275 >>> country_code('GB', iso_format='alpha3')
276 ValidationError(func=country_code, args={'value': 'GB', 'iso_format': 'alpha3'})
277 >>> country_code('USA')
278 True
279 >>> country_code('840', iso_format='numeric')
280 True
281 >>> country_code('iN', iso_format='alpha2')
282 ValidationError(func=country_code, args={'value': 'iN', 'iso_format': 'alpha2'})
283 >>> country_code('ZWE', iso_format='alpha3')
284 True
285
286 Args:
287 value:
288 Country code string to validate.
289 iso_format:
290 ISO format to be used. Available options are:
291 `auto`, `alpha2`, `alpha3` and `numeric`.
292 ignore_case:
293 Enable/Disable case-sensitive matching.
294
295 Returns:
296 (Literal[True]): If `value` is a valid country code.
297 (ValidationError): If `value` is an invalid country code.
298 """
299 if not value:
300 return False
301
302 if not (1 < len(value) < 4):
303 return False
304
305 if iso_format == "auto" and (iso_format := _get_code_type(value)) == "invalid":
306 return False
307
308 if iso_format == "alpha2":
309 return (
310 value.upper() in set(_alpha3_to_alpha2.values())
311 if ignore_case
312 else value in set(_alpha3_to_alpha2.values())
313 )
314 if iso_format == "alpha3":
315 return value.upper() in _alpha3_to_alpha2 if ignore_case else value in _alpha3_to_alpha2
316
317 return value in _numeric if iso_format == "numeric" else False
318
319
320@validator
321def currency(value: str, /, *, skip_symbols: bool = True, ignore_case: bool = False):
322 """Validates given currency code.
323
324 This performs [ISO 4217][1] currency code/symbol validation.
325
326 [1]: https://www.iso.org/iso-4217-currency-codes.html
327
328 Examples:
329 >>> currency('USD')
330 True
331 >>> currency('ZWX')
332 ValidationError(func=currency, args={'value': 'ZWX'})
333
334 Args:
335 value:
336 Currency code/symbol string to validate.
337 skip_symbols:
338 Skip currency symbol validation.
339 ignore_case:
340 Enable/Disable case-sensitive matching.
341
342 Returns:
343 (Literal[True]): If `value` is a valid currency code.
344 (ValidationError): If `value` is an invalid currency code.
345 """
346 if not value:
347 return False
348
349 if not skip_symbols and value in _currency_symbols:
350 return True
351
352 if len(value) != 3:
353 return False
354
355 return value.upper() in _currency_iso4217 if ignore_case else value in _currency_iso4217