currencies_macros/
lib.rs

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, &currency)?,
64        "BTC" => parse_amount::<BTC, SAFE>(&amount, &currency)?,
65        "ETH" => parse_amount::<ETH, SAFE>(&amount, &currency)?,
66        "SOL" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
67        "ALGO" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
68        "ORCA" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
69        "AVAX" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
70        "ZEC" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
71        "XMR" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
72        "DOGE" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
73        "LTC" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
74        "MATIC" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
75        "XLM" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
76        "TAO" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
77        "NEAR" => parse_amount::<SOL, SAFE>(&amount, &currency)?,
78        "AAVE" => parse_amount::<AAVE, SAFE>(&amount, &currency)?,
79        "ADA" => parse_amount::<ADA, SAFE>(&amount, &currency)?,
80        "BOOK" => parse_amount::<BOOK, SAFE>(&amount, &currency)?,
81        "DOT" => parse_amount::<DOT, SAFE>(&amount, &currency)?,
82        "KSM" => parse_amount::<KSM, SAFE>(&amount, &currency)?,
83        "USD" => parse_amount::<USD, SAFE>(&amount, &currency)?,
84        "BAM" => parse_amount::<BAM, SAFE>(&amount, &currency)?,
85        "AED" => parse_amount::<AED, SAFE>(&amount, &currency)?,
86        "AFN" => parse_amount::<AFN, SAFE>(&amount, &currency)?,
87        "ALL" => parse_amount::<ALL, SAFE>(&amount, &currency)?,
88        "AMD" => parse_amount::<AMD, SAFE>(&amount, &currency)?,
89        "ANG" => parse_amount::<ANG, SAFE>(&amount, &currency)?,
90        "AOA" => parse_amount::<AOA, SAFE>(&amount, &currency)?,
91        "ARS" => parse_amount::<ARS, SAFE>(&amount, &currency)?,
92        "AUD" => parse_amount::<AUD, SAFE>(&amount, &currency)?,
93        "AWG" => parse_amount::<AWG, SAFE>(&amount, &currency)?,
94        "AZN" => parse_amount::<AZN, SAFE>(&amount, &currency)?,
95        "BBD" => parse_amount::<BBD, SAFE>(&amount, &currency)?,
96        "BDT" => parse_amount::<BDT, SAFE>(&amount, &currency)?,
97        "BGN" => parse_amount::<BGN, SAFE>(&amount, &currency)?,
98        "BHD" => parse_amount::<BHD, SAFE>(&amount, &currency)?,
99        "BIF" => parse_amount::<BIF, SAFE>(&amount, &currency)?,
100        "BMD" => parse_amount::<BMD, SAFE>(&amount, &currency)?,
101        "BND" => parse_amount::<BND, SAFE>(&amount, &currency)?,
102        "BOB" => parse_amount::<BOB, SAFE>(&amount, &currency)?,
103        "BOV" => parse_amount::<BOV, SAFE>(&amount, &currency)?,
104        "BRL" => parse_amount::<BRL, SAFE>(&amount, &currency)?,
105        "BSD" => parse_amount::<BSD, SAFE>(&amount, &currency)?,
106        "BTN" => parse_amount::<BTN, SAFE>(&amount, &currency)?,
107        "BWP" => parse_amount::<BWP, SAFE>(&amount, &currency)?,
108        "BYN" => parse_amount::<BYN, SAFE>(&amount, &currency)?,
109        "BZD" => parse_amount::<BZD, SAFE>(&amount, &currency)?,
110        "CAD" => parse_amount::<CAD, SAFE>(&amount, &currency)?,
111        "CDF" => parse_amount::<CDF, SAFE>(&amount, &currency)?,
112        "CHE" => parse_amount::<CHE, SAFE>(&amount, &currency)?,
113        "CHF" => parse_amount::<CHF, SAFE>(&amount, &currency)?,
114        "CHW" => parse_amount::<CHW, SAFE>(&amount, &currency)?,
115        "CLF" => parse_amount::<CLF, SAFE>(&amount, &currency)?,
116        "CLP" => parse_amount::<CLP, SAFE>(&amount, &currency)?,
117        "COP" => parse_amount::<COP, SAFE>(&amount, &currency)?,
118        "COU" => parse_amount::<COU, SAFE>(&amount, &currency)?,
119        "CRC" => parse_amount::<CRC, SAFE>(&amount, &currency)?,
120        "CUC" => parse_amount::<CUC, SAFE>(&amount, &currency)?,
121        "CUP" => parse_amount::<CUP, SAFE>(&amount, &currency)?,
122        "CVE" => parse_amount::<CVE, SAFE>(&amount, &currency)?,
123        "CZK" => parse_amount::<CZK, SAFE>(&amount, &currency)?,
124        "DJF" => parse_amount::<DJF, SAFE>(&amount, &currency)?,
125        "DKK" => parse_amount::<DKK, SAFE>(&amount, &currency)?,
126        "DOP" => parse_amount::<DOP, SAFE>(&amount, &currency)?,
127        "DZD" => parse_amount::<DZD, SAFE>(&amount, &currency)?,
128        "EGP" => parse_amount::<EGP, SAFE>(&amount, &currency)?,
129        "ERN" => parse_amount::<ERN, SAFE>(&amount, &currency)?,
130        "ETB" => parse_amount::<ETB, SAFE>(&amount, &currency)?,
131        "EUR" => parse_amount::<EUR, SAFE>(&amount, &currency)?,
132        "FJD" => parse_amount::<FJD, SAFE>(&amount, &currency)?,
133        "FKP" => parse_amount::<FKP, SAFE>(&amount, &currency)?,
134        "GBP" => parse_amount::<GBP, SAFE>(&amount, &currency)?,
135        "GEL" => parse_amount::<GEL, SAFE>(&amount, &currency)?,
136        "GHS" => parse_amount::<GHS, SAFE>(&amount, &currency)?,
137        "GIP" => parse_amount::<GIP, SAFE>(&amount, &currency)?,
138        "GMD" => parse_amount::<GMD, SAFE>(&amount, &currency)?,
139        "GNF" => parse_amount::<GNF, SAFE>(&amount, &currency)?,
140        "GTQ" => parse_amount::<GTQ, SAFE>(&amount, &currency)?,
141        "HKD" => parse_amount::<HKD, SAFE>(&amount, &currency)?,
142        "HNL" => parse_amount::<HNL, SAFE>(&amount, &currency)?,
143        "HTG" => parse_amount::<HTG, SAFE>(&amount, &currency)?,
144        "HUF" => parse_amount::<HUF, SAFE>(&amount, &currency)?,
145        "IDR" => parse_amount::<IDR, SAFE>(&amount, &currency)?,
146        "ILS" => parse_amount::<ILS, SAFE>(&amount, &currency)?,
147        "INR" => parse_amount::<INR, SAFE>(&amount, &currency)?,
148        "IQD" => parse_amount::<IQD, SAFE>(&amount, &currency)?,
149        "IRR" => parse_amount::<IRR, SAFE>(&amount, &currency)?,
150        "ISK" => parse_amount::<ISK, SAFE>(&amount, &currency)?,
151        "JMD" => parse_amount::<JMD, SAFE>(&amount, &currency)?,
152        "JOD" => parse_amount::<JOD, SAFE>(&amount, &currency)?,
153        "JPY" => parse_amount::<JPY, SAFE>(&amount, &currency)?,
154        "KES" => parse_amount::<KES, SAFE>(&amount, &currency)?,
155        "KGS" => parse_amount::<KGS, SAFE>(&amount, &currency)?,
156        "KHR" => parse_amount::<KHR, SAFE>(&amount, &currency)?,
157        "KMF" => parse_amount::<KMF, SAFE>(&amount, &currency)?,
158        "KPW" => parse_amount::<KPW, SAFE>(&amount, &currency)?,
159        "KRW" => parse_amount::<KRW, SAFE>(&amount, &currency)?,
160        "KWD" => parse_amount::<KWD, SAFE>(&amount, &currency)?,
161        "KYD" => parse_amount::<KYD, SAFE>(&amount, &currency)?,
162        "KZT" => parse_amount::<KZT, SAFE>(&amount, &currency)?,
163        "LAK" => parse_amount::<LAK, SAFE>(&amount, &currency)?,
164        "LBP" => parse_amount::<LBP, SAFE>(&amount, &currency)?,
165        "LKR" => parse_amount::<LKR, SAFE>(&amount, &currency)?,
166        "LRD" => parse_amount::<LRD, SAFE>(&amount, &currency)?,
167        "LSL" => parse_amount::<LSL, SAFE>(&amount, &currency)?,
168        "LYD" => parse_amount::<LYD, SAFE>(&amount, &currency)?,
169        "MAD" => parse_amount::<MAD, SAFE>(&amount, &currency)?,
170        "MDL" => parse_amount::<MDL, SAFE>(&amount, &currency)?,
171        "MGA" => parse_amount::<MGA, SAFE>(&amount, &currency)?,
172        "MKD" => parse_amount::<MKD, SAFE>(&amount, &currency)?,
173        "MMK" => parse_amount::<MMK, SAFE>(&amount, &currency)?,
174        "MNT" => parse_amount::<MNT, SAFE>(&amount, &currency)?,
175        "MOP" => parse_amount::<MOP, SAFE>(&amount, &currency)?,
176        "MRU" => parse_amount::<MRU, SAFE>(&amount, &currency)?,
177        "MUR" => parse_amount::<MUR, SAFE>(&amount, &currency)?,
178        "MVR" => parse_amount::<MVR, SAFE>(&amount, &currency)?,
179        "MWK" => parse_amount::<MWK, SAFE>(&amount, &currency)?,
180        "MXN" => parse_amount::<MXN, SAFE>(&amount, &currency)?,
181        "MXV" => parse_amount::<MXV, SAFE>(&amount, &currency)?,
182        "MYR" => parse_amount::<MYR, SAFE>(&amount, &currency)?,
183        "MZN" => parse_amount::<MZN, SAFE>(&amount, &currency)?,
184        "NAD" => parse_amount::<NAD, SAFE>(&amount, &currency)?,
185        "NGN" => parse_amount::<NGN, SAFE>(&amount, &currency)?,
186        "NIO" => parse_amount::<NIO, SAFE>(&amount, &currency)?,
187        "NOK" => parse_amount::<NOK, SAFE>(&amount, &currency)?,
188        "NPR" => parse_amount::<NPR, SAFE>(&amount, &currency)?,
189        "NZD" => parse_amount::<NZD, SAFE>(&amount, &currency)?,
190        "OMR" => parse_amount::<OMR, SAFE>(&amount, &currency)?,
191        "PAB" => parse_amount::<PAB, SAFE>(&amount, &currency)?,
192        "PEN" => parse_amount::<PEN, SAFE>(&amount, &currency)?,
193        "PGK" => parse_amount::<PGK, SAFE>(&amount, &currency)?,
194        "PHP" => parse_amount::<PHP, SAFE>(&amount, &currency)?,
195        "PKR" => parse_amount::<PKR, SAFE>(&amount, &currency)?,
196        "PLN" => parse_amount::<PLN, SAFE>(&amount, &currency)?,
197        "PYG" => parse_amount::<PYG, SAFE>(&amount, &currency)?,
198        "QAR" => parse_amount::<QAR, SAFE>(&amount, &currency)?,
199        "RON" => parse_amount::<RON, SAFE>(&amount, &currency)?,
200        "RSD" => parse_amount::<RSD, SAFE>(&amount, &currency)?,
201        "CNY" => parse_amount::<CNY, SAFE>(&amount, &currency)?,
202        "RUB" => parse_amount::<RUB, SAFE>(&amount, &currency)?,
203        "RWF" => parse_amount::<RWF, SAFE>(&amount, &currency)?,
204        "SAR" => parse_amount::<SAR, SAFE>(&amount, &currency)?,
205        "SBD" => parse_amount::<SBD, SAFE>(&amount, &currency)?,
206        "SCR" => parse_amount::<SCR, SAFE>(&amount, &currency)?,
207        "SDG" => parse_amount::<SDG, SAFE>(&amount, &currency)?,
208        "SEK" => parse_amount::<SEK, SAFE>(&amount, &currency)?,
209        "SGD" => parse_amount::<SGD, SAFE>(&amount, &currency)?,
210        "SHP" => parse_amount::<SHP, SAFE>(&amount, &currency)?,
211        "SLE" => parse_amount::<SLE, SAFE>(&amount, &currency)?,
212        "SOS" => parse_amount::<SOS, SAFE>(&amount, &currency)?,
213        "SRD" => parse_amount::<SRD, SAFE>(&amount, &currency)?,
214        "SSP" => parse_amount::<SSP, SAFE>(&amount, &currency)?,
215        "STN" => parse_amount::<STN, SAFE>(&amount, &currency)?,
216        "SYP" => parse_amount::<SYP, SAFE>(&amount, &currency)?,
217        "SZL" => parse_amount::<SZL, SAFE>(&amount, &currency)?,
218        "THB" => parse_amount::<THB, SAFE>(&amount, &currency)?,
219        "TJS" => parse_amount::<TJS, SAFE>(&amount, &currency)?,
220        "TMT" => parse_amount::<TMT, SAFE>(&amount, &currency)?,
221        "TND" => parse_amount::<TND, SAFE>(&amount, &currency)?,
222        "TOP" => parse_amount::<TOP, SAFE>(&amount, &currency)?,
223        "TRY" => parse_amount::<TRY, SAFE>(&amount, &currency)?,
224        "TTD" => parse_amount::<TTD, SAFE>(&amount, &currency)?,
225        "TWD" => parse_amount::<TWD, SAFE>(&amount, &currency)?,
226        "TZS" => parse_amount::<TZS, SAFE>(&amount, &currency)?,
227        "UAH" => parse_amount::<UAH, SAFE>(&amount, &currency)?,
228        "UGX" => parse_amount::<UGX, SAFE>(&amount, &currency)?,
229        "UYU" => parse_amount::<UYU, SAFE>(&amount, &currency)?,
230        "UZS" => parse_amount::<UZS, SAFE>(&amount, &currency)?,
231        "VED" => parse_amount::<VED, SAFE>(&amount, &currency)?,
232        "VES" => parse_amount::<VES, SAFE>(&amount, &currency)?,
233        "VND" => parse_amount::<VND, SAFE>(&amount, &currency)?,
234        "VUV" => parse_amount::<VUV, SAFE>(&amount, &currency)?,
235        "WST" => parse_amount::<WST, SAFE>(&amount, &currency)?,
236        "XAF" => parse_amount::<XAF, SAFE>(&amount, &currency)?,
237        "XAG" => parse_amount::<XAG, SAFE>(&amount, &currency)?,
238        "XAU" => parse_amount::<XAU, SAFE>(&amount, &currency)?,
239        "XCD" => parse_amount::<XCD, SAFE>(&amount, &currency)?,
240        "XOF" => parse_amount::<XOF, SAFE>(&amount, &currency)?,
241        "XPD" => parse_amount::<XPD, SAFE>(&amount, &currency)?,
242        "XPF" => parse_amount::<XPF, SAFE>(&amount, &currency)?,
243        "XPT" => parse_amount::<XPT, SAFE>(&amount, &currency)?,
244        "YER" => parse_amount::<YER, SAFE>(&amount, &currency)?,
245        "ZAR" => parse_amount::<ZAR, SAFE>(&amount, &currency)?,
246        "ZMW" => parse_amount::<ZMW, SAFE>(&amount, &currency)?,
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}