custom_currency/
custom_currency.rs

1//! Custom currency definition examples.
2//!
3//! This example demonstrates how to define your own currencies and use them
4//! with the typed-money library. This is useful for currencies not included
5//! in the library or for custom tokens/units.
6//!
7//! Run with: `cargo run --example custom_currency`
8
9#![allow(clippy::upper_case_acronyms)]
10
11use typed_money::{Amount, Currency, Rate, RoundingMode};
12
13// ========================================
14// Define Custom Fiat Currencies
15// ========================================
16
17/// Canadian Dollar
18#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
19struct CAD;
20
21impl Currency for CAD {
22    const DECIMALS: u8 = 2;
23    const CODE: &'static str = "CAD";
24    const SYMBOL: &'static str = "C$";
25}
26
27/// Swiss Franc
28#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
29struct CHF;
30
31impl Currency for CHF {
32    const DECIMALS: u8 = 2;
33    const CODE: &'static str = "CHF";
34    const SYMBOL: &'static str = "CHF";
35}
36
37/// Australian Dollar
38#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
39struct AUD;
40
41impl Currency for AUD {
42    const DECIMALS: u8 = 2;
43    const CODE: &'static str = "AUD";
44    const SYMBOL: &'static str = "A$";
45}
46
47// ========================================
48// Define Cryptocurrency
49// ========================================
50
51/// Dogecoin - cryptocurrency with 8 decimal places
52#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
53struct DOGE;
54
55impl Currency for DOGE {
56    const DECIMALS: u8 = 8;
57    const CODE: &'static str = "DOGE";
58    const SYMBOL: &'static str = "Ð";
59}
60
61// ========================================
62// Define Game/Virtual Currency
63// ========================================
64
65/// Gold coins in a video game (no fractional coins)
66#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
67struct GOLD;
68
69impl Currency for GOLD {
70    const DECIMALS: u8 = 0;
71    const CODE: &'static str = "GOLD";
72    const SYMBOL: &'static str = "⚜";
73}
74
75/// Premium gems with fractional amounts (for precise calculations)
76#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
77struct GEMS;
78
79impl Currency for GEMS {
80    const DECIMALS: u8 = 2;
81    const CODE: &'static str = "GEMS";
82    const SYMBOL: &'static str = "💎";
83}
84
85// ========================================
86// Define Loyalty Points
87// ========================================
88
89/// Reward points (with fractional points for precise calculations)
90#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
91struct POINTS;
92
93impl Currency for POINTS {
94    const DECIMALS: u8 = 2;
95    const CODE: &'static str = "POINTS";
96    const SYMBOL: &'static str = "★";
97}
98
99fn main() {
100    println!("=== Custom Currency Examples ===\n");
101
102    // ========================================
103    // Using Custom Fiat Currencies
104    // ========================================
105    println!("1. CUSTOM FIAT CURRENCIES");
106    println!("-------------------------");
107
108    let cad = Amount::<CAD>::from_major(100);
109    let chf = Amount::<CHF>::from_major(85);
110    let aud = Amount::<AUD>::from_major(150);
111
112    println!("Canadian Dollar: {}", cad);
113    println!("Swiss Franc:     {}", chf);
114    println!("Australian Dollar: {}", aud);
115
116    // Arithmetic works the same
117    let total_cad = cad + Amount::<CAD>::from_major(50);
118    println!("\nC$100 + C$50 = {}", total_cad);
119
120    // ========================================
121    // Currency Properties
122    // ========================================
123    println!("\n2. CURRENCY PROPERTIES");
124    println!("----------------------");
125
126    println!(
127        "CAD: code={}, symbol={}, decimals={}",
128        CAD::CODE,
129        CAD::SYMBOL,
130        CAD::DECIMALS
131    );
132    println!(
133        "CHF: code={}, symbol={}, decimals={}",
134        CHF::CODE,
135        CHF::SYMBOL,
136        CHF::DECIMALS
137    );
138    println!(
139        "DOGE: code={}, symbol={}, decimals={}",
140        DOGE::CODE,
141        DOGE::SYMBOL,
142        DOGE::DECIMALS
143    );
144    println!(
145        "GOLD: code={}, symbol={}, decimals={}",
146        GOLD::CODE,
147        GOLD::SYMBOL,
148        GOLD::DECIMALS
149    );
150
151    // ========================================
152    // Converting Between Custom Currencies
153    // ========================================
154    println!("\n3. CONVERTING CUSTOM CURRENCIES");
155    println!("--------------------------------");
156
157    let _cad_amount = Amount::<CAD>::from_major(100);
158    let cad_to_aud = Rate::<CAD, AUD>::new(1.08); // 1 CAD = 1.08 AUD
159
160    println!("Example: C$100 at rate {} CAD→AUD", cad_to_aud.value());
161
162    // ========================================
163    // Cryptocurrency Example
164    // ========================================
165    println!("\n4. CRYPTOCURRENCY (DOGECOIN)");
166    println!("-----------------------------");
167
168    // Dogecoin with 8 decimal places (like Bitcoin)
169    let doge = Amount::<DOGE>::from_minor(100_000_000); // 1 DOGE
170    println!("Amount: {} (1 DOGE)", doge);
171
172    let small_amount = Amount::<DOGE>::from_minor(12_345_678); // 0.12345678 DOGE
173    println!("Small amount: {} (0.12345678 DOGE)", small_amount);
174
175    let total = doge + small_amount;
176    println!("Total: {}", total);
177
178    // ========================================
179    // Video Game Currency
180    // ========================================
181    println!("\n5. VIDEO GAME CURRENCIES");
182    println!("------------------------");
183
184    // Gold coins (no fractional amounts)
185    let gold = Amount::<GOLD>::from_major(1000);
186    println!("Gold coins: {}", gold);
187
188    let loot = Amount::<GOLD>::from_major(150);
189    let total_gold = gold + loot;
190    println!(
191        "After looting: {} (was {}, found {})",
192        total_gold, gold, loot
193    );
194
195    // Premium gems (with fractional amounts for bonuses)
196    let gems = Amount::<GEMS>::from_major(50);
197    println!("\nPremium gems: {}", gems);
198
199    // 10% bonus
200    let bonus = (gems * 10) / 100;
201    println!("Bonus (10%): {}", bonus);
202
203    let total_gems = gems + bonus;
204    println!("Total with bonus: {}", total_gems);
205
206    // ========================================
207    // Loyalty Points System
208    // ========================================
209    println!("\n6. LOYALTY POINTS SYSTEM");
210    println!("------------------------");
211
212    let purchase = Amount::<POINTS>::from_major(100);
213    println!("Purchase: {}", purchase);
214
215    // Earn 5% back in points
216    let earn_rate = 5;
217    let points_earned = (purchase * earn_rate) / 100;
218    println!("Points earned ({}%): {}", earn_rate, points_earned);
219
220    // Accumulate points
221    let existing_points = Amount::<POINTS>::from_major(237);
222    let new_balance = existing_points + points_earned;
223    println!("\nPrevious balance: {}", existing_points);
224    println!("New balance: {}", new_balance);
225
226    // Redeem points
227    let redemption = Amount::<POINTS>::from_major(50);
228    let final_balance = new_balance - redemption;
229    println!("\nAfter redeeming {}: {}", redemption, final_balance);
230
231    // ========================================
232    // Exchange Rate Between Custom Currencies
233    // ========================================
234    println!("\n7. EXCHANGE RATES BETWEEN CUSTOM CURRENCIES");
235    println!("--------------------------------------------");
236
237    // Define exchange rates
238    let cad_to_aud = Rate::<CAD, AUD>::new(1.08); // 1 CAD = 1.08 AUD
239
240    let cad_amount = Amount::<CAD>::from_major(100);
241    println!("Canadian Dollars: {}", cad_amount);
242    println!("Exchange rate: 1 CAD = {} AUD", cad_to_aud.value());
243
244    let aud_amount = cad_amount.convert(&cad_to_aud);
245    println!("Australian Dollars: {}", aud_amount);
246
247    // ========================================
248    // Type Safety Still Works
249    // ========================================
250    println!("\n8. TYPE SAFETY");
251    println!("--------------");
252
253    println!("✓ Can add CAD + CAD");
254    println!("✓ Can add GOLD + GOLD");
255    println!("✗ Cannot add CAD + CHF (compile error!)");
256    println!("✗ Cannot add GOLD + GEMS (compile error!)");
257    println!("\nCustom currencies have the same type safety as built-in ones!");
258
259    // These would NOT compile:
260    // let invalid = cad + chf;      // Compile error: different currencies
261    // let invalid2 = gold + gems;   // Compile error: different currencies
262
263    // ========================================
264    // Rounding with Custom Currencies
265    // ========================================
266    println!("\n9. ROUNDING");
267    println!("-----------");
268
269    let cad = Amount::<CAD>::from_major(100) / 3; // C$33.333...
270    println!("CAD (exact): {}", cad);
271    println!("CAD (rounded): {}", cad.round(RoundingMode::HalfUp));
272
273    let doge = Amount::<DOGE>::from_minor(100_000_000) / 3; // 0.33333333 DOGE
274    println!("\nDOGE (exact): {}", doge);
275    println!("DOGE (rounded): {}", doge.round(RoundingMode::HalfUp));
276
277    let gold = Amount::<GOLD>::from_major(1000) / 3; // 333.333... coins
278    println!("\nGOLD (exact): {}", gold);
279    println!(
280        "GOLD (rounded): {} (no decimals)",
281        gold.round(RoundingMode::HalfUp)
282    );
283
284    // ========================================
285    // Real-World: Multi-Currency Wallet
286    // ========================================
287    println!("\n10. REAL-WORLD: MULTI-CURRENCY WALLET");
288    println!("--------------------------------------");
289
290    println!("Wallet balances:");
291    let cad_balance = Amount::<CAD>::from_major(500);
292    let aud_balance = Amount::<AUD>::from_major(300);
293    let chf_balance = Amount::<CHF>::from_major(200);
294
295    println!("  CAD: {}", cad_balance);
296    println!("  AUD: {}", aud_balance);
297    println!("  CHF: {}", chf_balance);
298
299    println!("\nTransaction: Spend C$50");
300    let new_cad = cad_balance - Amount::<CAD>::from_major(50);
301    println!("  New CAD balance: {}", new_cad);
302
303    println!("\nType system ensures you can't accidentally mix currencies!");
304
305    println!("\n=== All custom currency examples completed! ===");
306}