1use currencies_core::{
2 currency::*,
3 safety::{Checked, Unchecked},
4 Currency, ParsedAmount,
5};
6use derive_syn_parse::Parse;
7use proc_macro::TokenStream;
8use proc_macro2::TokenStream as TokenStream2;
9use quote::quote;
10use syn::{parse2, Error, Ident, LitStr, Result, Token};
11
12#[derive(Parse)]
13struct AmountInput {
14 currency: Ident,
15 _comma: Token![,],
16 amount: LitStr,
17}
18
19#[proc_macro]
20pub fn amt(input: TokenStream) -> TokenStream {
21 match amt_internal::<false>(input) {
22 Ok(tokens) => tokens.into(),
23 Err(err) => err.to_compile_error().into(),
24 }
25}
26
27#[proc_macro]
28pub fn amt_checked(input: TokenStream) -> TokenStream {
29 match amt_internal::<true>(input) {
30 Ok(tokens) => tokens.into(),
31 Err(err) => err.to_compile_error().into(),
32 }
33}
34
35fn filter_error(err: impl ToString) -> String {
36 format!("invalid amount: {}", err.to_string().trim_start_matches("error: "))
37}
38
39fn parse_amount<C: Currency, const SAFE: bool>(amount: &LitStr, currency_ident: &Ident) -> Result<TokenStream2> {
40 let krate = quote!(::currencies);
41 if SAFE {
42 let amount: ParsedAmount<C, Checked> = amount.value().parse().map_err(|err| Error::new(amount.span(), filter_error(err)))?;
43 let amount = amount.amount;
44 let amount: TokenStream2 = format!("{:?}", amount.raw_backing()).parse().unwrap();
45 Ok(quote! {
46 #krate::Amount::<#currency_ident, #krate::safety::Checked>::from_raw(#amount)
47 })
48 } else {
49 let amount: ParsedAmount<C, Unchecked> = amount.value().parse().map_err(|err| Error::new(amount.span(), filter_error(err)))?;
50 let amount = amount.amount;
51 let amount: TokenStream2 = format!("{:?}", amount.raw_backing()).parse().unwrap();
52 Ok(quote! {
53 #krate::Amount::<#currency_ident, #krate::safety::Unchecked>::from_raw(#amount)
54 })
55 }
56}
57
58fn amt_internal<const SAFE: bool>(tokens: impl Into<TokenStream2>) -> Result<TokenStream2> {
59 let input = parse2::<AmountInput>(tokens.into())?;
60 let currency = input.currency;
61 let amount = input.amount;
62 let output = match currency.to_string().to_uppercase().as_str() {
63 "USDC" => parse_amount::<USDC, SAFE>(&amount, ¤cy)?,
64 "BTC" => parse_amount::<BTC, SAFE>(&amount, ¤cy)?,
65 "ETH" => parse_amount::<ETH, SAFE>(&amount, ¤cy)?,
66 "SOL" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
67 "ALGO" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
68 "ORCA" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
69 "AVAX" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
70 "ZEC" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
71 "XMR" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
72 "DOGE" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
73 "LTC" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
74 "MATIC" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
75 "XLM" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
76 "TAO" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
77 "NEAR" => parse_amount::<SOL, SAFE>(&amount, ¤cy)?,
78 "AAVE" => parse_amount::<AAVE, SAFE>(&amount, ¤cy)?,
79 "ADA" => parse_amount::<ADA, SAFE>(&amount, ¤cy)?,
80 "BOOK" => parse_amount::<BOOK, SAFE>(&amount, ¤cy)?,
81 "DOT" => parse_amount::<DOT, SAFE>(&amount, ¤cy)?,
82 "KSM" => parse_amount::<KSM, SAFE>(&amount, ¤cy)?,
83 "USD" => parse_amount::<USD, SAFE>(&amount, ¤cy)?,
84 "BAM" => parse_amount::<BAM, SAFE>(&amount, ¤cy)?,
85 "AED" => parse_amount::<AED, SAFE>(&amount, ¤cy)?,
86 "AFN" => parse_amount::<AFN, SAFE>(&amount, ¤cy)?,
87 "ALL" => parse_amount::<ALL, SAFE>(&amount, ¤cy)?,
88 "AMD" => parse_amount::<AMD, SAFE>(&amount, ¤cy)?,
89 "ANG" => parse_amount::<ANG, SAFE>(&amount, ¤cy)?,
90 "AOA" => parse_amount::<AOA, SAFE>(&amount, ¤cy)?,
91 "ARS" => parse_amount::<ARS, SAFE>(&amount, ¤cy)?,
92 "AUD" => parse_amount::<AUD, SAFE>(&amount, ¤cy)?,
93 "AWG" => parse_amount::<AWG, SAFE>(&amount, ¤cy)?,
94 "AZN" => parse_amount::<AZN, SAFE>(&amount, ¤cy)?,
95 "BBD" => parse_amount::<BBD, SAFE>(&amount, ¤cy)?,
96 "BDT" => parse_amount::<BDT, SAFE>(&amount, ¤cy)?,
97 "BGN" => parse_amount::<BGN, SAFE>(&amount, ¤cy)?,
98 "BHD" => parse_amount::<BHD, SAFE>(&amount, ¤cy)?,
99 "BIF" => parse_amount::<BIF, SAFE>(&amount, ¤cy)?,
100 "BMD" => parse_amount::<BMD, SAFE>(&amount, ¤cy)?,
101 "BND" => parse_amount::<BND, SAFE>(&amount, ¤cy)?,
102 "BOB" => parse_amount::<BOB, SAFE>(&amount, ¤cy)?,
103 "BOV" => parse_amount::<BOV, SAFE>(&amount, ¤cy)?,
104 "BRL" => parse_amount::<BRL, SAFE>(&amount, ¤cy)?,
105 "BSD" => parse_amount::<BSD, SAFE>(&amount, ¤cy)?,
106 "BTN" => parse_amount::<BTN, SAFE>(&amount, ¤cy)?,
107 "BWP" => parse_amount::<BWP, SAFE>(&amount, ¤cy)?,
108 "BYN" => parse_amount::<BYN, SAFE>(&amount, ¤cy)?,
109 "BZD" => parse_amount::<BZD, SAFE>(&amount, ¤cy)?,
110 "CAD" => parse_amount::<CAD, SAFE>(&amount, ¤cy)?,
111 "CDF" => parse_amount::<CDF, SAFE>(&amount, ¤cy)?,
112 "CHE" => parse_amount::<CHE, SAFE>(&amount, ¤cy)?,
113 "CHF" => parse_amount::<CHF, SAFE>(&amount, ¤cy)?,
114 "CHW" => parse_amount::<CHW, SAFE>(&amount, ¤cy)?,
115 "CLF" => parse_amount::<CLF, SAFE>(&amount, ¤cy)?,
116 "CLP" => parse_amount::<CLP, SAFE>(&amount, ¤cy)?,
117 "COP" => parse_amount::<COP, SAFE>(&amount, ¤cy)?,
118 "COU" => parse_amount::<COU, SAFE>(&amount, ¤cy)?,
119 "CRC" => parse_amount::<CRC, SAFE>(&amount, ¤cy)?,
120 "CUC" => parse_amount::<CUC, SAFE>(&amount, ¤cy)?,
121 "CUP" => parse_amount::<CUP, SAFE>(&amount, ¤cy)?,
122 "CVE" => parse_amount::<CVE, SAFE>(&amount, ¤cy)?,
123 "CZK" => parse_amount::<CZK, SAFE>(&amount, ¤cy)?,
124 "DJF" => parse_amount::<DJF, SAFE>(&amount, ¤cy)?,
125 "DKK" => parse_amount::<DKK, SAFE>(&amount, ¤cy)?,
126 "DOP" => parse_amount::<DOP, SAFE>(&amount, ¤cy)?,
127 "DZD" => parse_amount::<DZD, SAFE>(&amount, ¤cy)?,
128 "EGP" => parse_amount::<EGP, SAFE>(&amount, ¤cy)?,
129 "ERN" => parse_amount::<ERN, SAFE>(&amount, ¤cy)?,
130 "ETB" => parse_amount::<ETB, SAFE>(&amount, ¤cy)?,
131 "EUR" => parse_amount::<EUR, SAFE>(&amount, ¤cy)?,
132 "FJD" => parse_amount::<FJD, SAFE>(&amount, ¤cy)?,
133 "FKP" => parse_amount::<FKP, SAFE>(&amount, ¤cy)?,
134 "GBP" => parse_amount::<GBP, SAFE>(&amount, ¤cy)?,
135 "GEL" => parse_amount::<GEL, SAFE>(&amount, ¤cy)?,
136 "GHS" => parse_amount::<GHS, SAFE>(&amount, ¤cy)?,
137 "GIP" => parse_amount::<GIP, SAFE>(&amount, ¤cy)?,
138 "GMD" => parse_amount::<GMD, SAFE>(&amount, ¤cy)?,
139 "GNF" => parse_amount::<GNF, SAFE>(&amount, ¤cy)?,
140 "GTQ" => parse_amount::<GTQ, SAFE>(&amount, ¤cy)?,
141 "HKD" => parse_amount::<HKD, SAFE>(&amount, ¤cy)?,
142 "HNL" => parse_amount::<HNL, SAFE>(&amount, ¤cy)?,
143 "HTG" => parse_amount::<HTG, SAFE>(&amount, ¤cy)?,
144 "HUF" => parse_amount::<HUF, SAFE>(&amount, ¤cy)?,
145 "IDR" => parse_amount::<IDR, SAFE>(&amount, ¤cy)?,
146 "ILS" => parse_amount::<ILS, SAFE>(&amount, ¤cy)?,
147 "INR" => parse_amount::<INR, SAFE>(&amount, ¤cy)?,
148 "IQD" => parse_amount::<IQD, SAFE>(&amount, ¤cy)?,
149 "IRR" => parse_amount::<IRR, SAFE>(&amount, ¤cy)?,
150 "ISK" => parse_amount::<ISK, SAFE>(&amount, ¤cy)?,
151 "JMD" => parse_amount::<JMD, SAFE>(&amount, ¤cy)?,
152 "JOD" => parse_amount::<JOD, SAFE>(&amount, ¤cy)?,
153 "JPY" => parse_amount::<JPY, SAFE>(&amount, ¤cy)?,
154 "KES" => parse_amount::<KES, SAFE>(&amount, ¤cy)?,
155 "KGS" => parse_amount::<KGS, SAFE>(&amount, ¤cy)?,
156 "KHR" => parse_amount::<KHR, SAFE>(&amount, ¤cy)?,
157 "KMF" => parse_amount::<KMF, SAFE>(&amount, ¤cy)?,
158 "KPW" => parse_amount::<KPW, SAFE>(&amount, ¤cy)?,
159 "KRW" => parse_amount::<KRW, SAFE>(&amount, ¤cy)?,
160 "KWD" => parse_amount::<KWD, SAFE>(&amount, ¤cy)?,
161 "KYD" => parse_amount::<KYD, SAFE>(&amount, ¤cy)?,
162 "KZT" => parse_amount::<KZT, SAFE>(&amount, ¤cy)?,
163 "LAK" => parse_amount::<LAK, SAFE>(&amount, ¤cy)?,
164 "LBP" => parse_amount::<LBP, SAFE>(&amount, ¤cy)?,
165 "LKR" => parse_amount::<LKR, SAFE>(&amount, ¤cy)?,
166 "LRD" => parse_amount::<LRD, SAFE>(&amount, ¤cy)?,
167 "LSL" => parse_amount::<LSL, SAFE>(&amount, ¤cy)?,
168 "LYD" => parse_amount::<LYD, SAFE>(&amount, ¤cy)?,
169 "MAD" => parse_amount::<MAD, SAFE>(&amount, ¤cy)?,
170 "MDL" => parse_amount::<MDL, SAFE>(&amount, ¤cy)?,
171 "MGA" => parse_amount::<MGA, SAFE>(&amount, ¤cy)?,
172 "MKD" => parse_amount::<MKD, SAFE>(&amount, ¤cy)?,
173 "MMK" => parse_amount::<MMK, SAFE>(&amount, ¤cy)?,
174 "MNT" => parse_amount::<MNT, SAFE>(&amount, ¤cy)?,
175 "MOP" => parse_amount::<MOP, SAFE>(&amount, ¤cy)?,
176 "MRU" => parse_amount::<MRU, SAFE>(&amount, ¤cy)?,
177 "MUR" => parse_amount::<MUR, SAFE>(&amount, ¤cy)?,
178 "MVR" => parse_amount::<MVR, SAFE>(&amount, ¤cy)?,
179 "MWK" => parse_amount::<MWK, SAFE>(&amount, ¤cy)?,
180 "MXN" => parse_amount::<MXN, SAFE>(&amount, ¤cy)?,
181 "MXV" => parse_amount::<MXV, SAFE>(&amount, ¤cy)?,
182 "MYR" => parse_amount::<MYR, SAFE>(&amount, ¤cy)?,
183 "MZN" => parse_amount::<MZN, SAFE>(&amount, ¤cy)?,
184 "NAD" => parse_amount::<NAD, SAFE>(&amount, ¤cy)?,
185 "NGN" => parse_amount::<NGN, SAFE>(&amount, ¤cy)?,
186 "NIO" => parse_amount::<NIO, SAFE>(&amount, ¤cy)?,
187 "NOK" => parse_amount::<NOK, SAFE>(&amount, ¤cy)?,
188 "NPR" => parse_amount::<NPR, SAFE>(&amount, ¤cy)?,
189 "NZD" => parse_amount::<NZD, SAFE>(&amount, ¤cy)?,
190 "OMR" => parse_amount::<OMR, SAFE>(&amount, ¤cy)?,
191 "PAB" => parse_amount::<PAB, SAFE>(&amount, ¤cy)?,
192 "PEN" => parse_amount::<PEN, SAFE>(&amount, ¤cy)?,
193 "PGK" => parse_amount::<PGK, SAFE>(&amount, ¤cy)?,
194 "PHP" => parse_amount::<PHP, SAFE>(&amount, ¤cy)?,
195 "PKR" => parse_amount::<PKR, SAFE>(&amount, ¤cy)?,
196 "PLN" => parse_amount::<PLN, SAFE>(&amount, ¤cy)?,
197 "PYG" => parse_amount::<PYG, SAFE>(&amount, ¤cy)?,
198 "QAR" => parse_amount::<QAR, SAFE>(&amount, ¤cy)?,
199 "RON" => parse_amount::<RON, SAFE>(&amount, ¤cy)?,
200 "RSD" => parse_amount::<RSD, SAFE>(&amount, ¤cy)?,
201 "CNY" => parse_amount::<CNY, SAFE>(&amount, ¤cy)?,
202 "RUB" => parse_amount::<RUB, SAFE>(&amount, ¤cy)?,
203 "RWF" => parse_amount::<RWF, SAFE>(&amount, ¤cy)?,
204 "SAR" => parse_amount::<SAR, SAFE>(&amount, ¤cy)?,
205 "SBD" => parse_amount::<SBD, SAFE>(&amount, ¤cy)?,
206 "SCR" => parse_amount::<SCR, SAFE>(&amount, ¤cy)?,
207 "SDG" => parse_amount::<SDG, SAFE>(&amount, ¤cy)?,
208 "SEK" => parse_amount::<SEK, SAFE>(&amount, ¤cy)?,
209 "SGD" => parse_amount::<SGD, SAFE>(&amount, ¤cy)?,
210 "SHP" => parse_amount::<SHP, SAFE>(&amount, ¤cy)?,
211 "SLE" => parse_amount::<SLE, SAFE>(&amount, ¤cy)?,
212 "SOS" => parse_amount::<SOS, SAFE>(&amount, ¤cy)?,
213 "SRD" => parse_amount::<SRD, SAFE>(&amount, ¤cy)?,
214 "SSP" => parse_amount::<SSP, SAFE>(&amount, ¤cy)?,
215 "STN" => parse_amount::<STN, SAFE>(&amount, ¤cy)?,
216 "SYP" => parse_amount::<SYP, SAFE>(&amount, ¤cy)?,
217 "SZL" => parse_amount::<SZL, SAFE>(&amount, ¤cy)?,
218 "THB" => parse_amount::<THB, SAFE>(&amount, ¤cy)?,
219 "TJS" => parse_amount::<TJS, SAFE>(&amount, ¤cy)?,
220 "TMT" => parse_amount::<TMT, SAFE>(&amount, ¤cy)?,
221 "TND" => parse_amount::<TND, SAFE>(&amount, ¤cy)?,
222 "TOP" => parse_amount::<TOP, SAFE>(&amount, ¤cy)?,
223 "TRY" => parse_amount::<TRY, SAFE>(&amount, ¤cy)?,
224 "TTD" => parse_amount::<TTD, SAFE>(&amount, ¤cy)?,
225 "TWD" => parse_amount::<TWD, SAFE>(&amount, ¤cy)?,
226 "TZS" => parse_amount::<TZS, SAFE>(&amount, ¤cy)?,
227 "UAH" => parse_amount::<UAH, SAFE>(&amount, ¤cy)?,
228 "UGX" => parse_amount::<UGX, SAFE>(&amount, ¤cy)?,
229 "UYU" => parse_amount::<UYU, SAFE>(&amount, ¤cy)?,
230 "UZS" => parse_amount::<UZS, SAFE>(&amount, ¤cy)?,
231 "VED" => parse_amount::<VED, SAFE>(&amount, ¤cy)?,
232 "VES" => parse_amount::<VES, SAFE>(&amount, ¤cy)?,
233 "VND" => parse_amount::<VND, SAFE>(&amount, ¤cy)?,
234 "VUV" => parse_amount::<VUV, SAFE>(&amount, ¤cy)?,
235 "WST" => parse_amount::<WST, SAFE>(&amount, ¤cy)?,
236 "XAF" => parse_amount::<XAF, SAFE>(&amount, ¤cy)?,
237 "XAG" => parse_amount::<XAG, SAFE>(&amount, ¤cy)?,
238 "XAU" => parse_amount::<XAU, SAFE>(&amount, ¤cy)?,
239 "XCD" => parse_amount::<XCD, SAFE>(&amount, ¤cy)?,
240 "XOF" => parse_amount::<XOF, SAFE>(&amount, ¤cy)?,
241 "XPD" => parse_amount::<XPD, SAFE>(&amount, ¤cy)?,
242 "XPF" => parse_amount::<XPF, SAFE>(&amount, ¤cy)?,
243 "XPT" => parse_amount::<XPT, SAFE>(&amount, ¤cy)?,
244 "YER" => parse_amount::<YER, SAFE>(&amount, ¤cy)?,
245 "ZAR" => parse_amount::<ZAR, SAFE>(&amount, ¤cy)?,
246 "ZMW" => parse_amount::<ZMW, SAFE>(&amount, ¤cy)?,
247 _ => {
248 return Err(Error::new(
249 currency.span(),
250 "Unsupported currency. The `amt!` macro only works for types defined by the `currencies` crate.",
251 ))
252 }
253 };
254 Ok(output)
255}