1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Mudra provides various currencies as distinct structs and each struct implements [`Currency`] trait.

macro_rules! international_currency_symbol {
	() => {
		"\u{00A4}"
	};
}

#[allow(private_bounds)]
pub trait Currency: Sealed {
	/// Returns the ISO 4217 code of the currency
	fn code(self) -> &'static str;

	/// Returns the name of the currency in English
	fn name(self) -> &'static str;

	/// Returns the ISO 4217 numeric code of the currency
	fn numeric(self) -> u16;

	#[doc = concat!(
		"Returns the commonly used symbol of the currency. If there is no symbol associated, the international currency symbol `",
		international_currency_symbol!(),
		"` is returned"
	)]
	fn symbol(self) -> &'static str;
}

trait Sealed {}

macro_rules! define_currency {
	(
		$currency:ident,
		$code:literal,
		$name:literal,
		$numeric:literal
		$(,)?
    ) => {
		#[doc = concat!($name, " (", international_currency_symbol!(), ")")]
		#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
		pub struct $currency;

		impl $currency {
			const CODE: &'static str = $code;

			const NAME: &'static str = $name;

			const NUMERIC: u16 = $numeric;

			const SYMBOL: &'static str = international_currency_symbol!();
		}

		impl Currency for $currency {
			#[doc = concat!("Returns", " `", $code, "`")]
			fn code(self) -> &'static str {
				Self::CODE
			}

			#[doc = concat!("Returns", " `", $name, "`")]
			fn name(self) -> &'static str {
				Self::NAME
			}

			#[doc = concat!("Returns", " `", $numeric, "`")]
			fn numeric(self) -> u16 {
				Self::NUMERIC
			}

			#[doc = concat!("Returns", " `", international_currency_symbol!(), "`")]
			fn symbol(self) -> &'static str {
				Self::SYMBOL
			}
		}

		impl Sealed for $currency {}
	};

	(
		$currency:ident,
		$code:literal,
		$name:literal,
		$numeric:literal,
		$symbol:literal
		$(,)?
    ) => {
		#[doc = concat!($name, " (", $symbol, ")")]
		#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
		pub struct $currency;

		impl $currency {
			const CODE: &'static str = $code;

			const NAME: &'static str = $name;

			const NUMERIC: u16 = $numeric;

			const SYMBOL: &'static str = $symbol;
		}

		impl Currency for $currency {
			#[doc = concat!("Returns", " `", $code, "`")]
			fn code(self) -> &'static str {
				Self::CODE
			}

			#[doc = concat!("Returns", " `", $name, "`")]
			fn name(self) -> &'static str {
				Self::NAME
			}

			#[doc = concat!("Returns", " `", $numeric, "`")]
			fn numeric(self) -> u16 {
				Self::NUMERIC
			}

			#[doc = concat!("Returns", " `", $symbol, "`")]
			fn symbol(self) -> &'static str {
				Self::SYMBOL
			}
		}

		impl Sealed for $currency {}
	};
}

define_currency!(AUD, "AUD", "Australian dollar", 036, "\u{0024}");
define_currency!(BOV, "BOV", "Bolivian Mvdol", 984);
define_currency!(CAD, "CAD", "Canadian dollar", 124, "\u{0024}");
define_currency!(CHF, "CHF", "Swiss franc", 756);
define_currency!(CNY, "CNY", "Renminbi", 156, "\u{00A5}");
define_currency!(EUR, "EUR", "Euro", 978, "\u{20AC}");
define_currency!(GBP, "GBP", "Pound sterling", 826, "\u{00A3}");
define_currency!(HKD, "HKD", "Hong Kong dollar", 344, "\u{0024}");
define_currency!(INR, "INR", "Indian rupee", 356, "\u{20B9}");
define_currency!(JPY, "JPY", "Japanese yen", 392, "\u{00A5}");
define_currency!(KRW, "KRW", "South Korean won", 410, "\u{20A9}");
define_currency!(MXN, "MXN", "Mexican peso", 484, "\u{0024}");
define_currency!(NOK, "NOK", "Norwegian krone", 578);
define_currency!(NZD, "NZD", "New Zealand dollar", 554, "\u{0024}");
define_currency!(SEK, "SEK", "Swedish krona", 752);
define_currency!(SGD, "SGD", "Singapore dollar", 702, "\u{0024}");
define_currency!(USD, "USD", "United States dollar", 840, "\u{0024}");

#[cfg(test)]
mod tests {
	use super::Currency;
	use super::{BOV, USD};

	#[test]
	fn has_code_name_numeric_and_international_currency_symbol() {
		let currency = BOV;

		assert_eq!(currency.code(), "BOV");
		assert_eq!(currency.name(), "Bolivian Mvdol");
		assert_eq!(currency.numeric(), 984);
		assert_eq!(currency.symbol(), "¤");
	}

	#[test]
	fn has_code_name_numeric_symbol() {
		let currency = USD;

		assert_eq!(currency.code(), "USD");
		assert_eq!(currency.name(), "United States dollar");
		assert_eq!(currency.numeric(), 840);
		assert_eq!(currency.symbol(), "$");
	}
}