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
use std::{collections::BTreeMap, fmt};
/// Represents amounts of any number of units.
#[derive(Clone, PartialEq, Default)]
pub struct Sum<Unit, Number>(pub(crate) BTreeMap<Unit, Number>)
where
    Unit: Ord;
impl<Unit, Number> Sum<Unit, Number>
where
    Unit: Ord,
{
    /// Sets the amount of a unit in a sum.
    pub fn set_amount_for_unit(&mut self, amount: Number, unit_: Unit) {
        self.0.insert(unit_, amount);
    }
    /// Gets the amounts of all units in undefined order.
    pub fn amounts(&self) -> impl Iterator<Item = (&Unit, &Number)> {
        self.0.iter()
    }
    pub fn unit_amount(&self, unit: &Unit) -> Option<&Number> {
        self.0.get(unit)
    }
}
impl<Unit, Number> fmt::Debug for Sum<Unit, Number>
where
    Unit: Ord + fmt::Debug,
    Number: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Sum(")?;
        f.debug_map().entries(self.0.iter()).finish()?;
        f.write_str(")")
    }
}
#[cfg(test)]
mod test {
    use super::Sum;
    use maplit::btreemap;
    #[test]
    fn default() {
        let actual = Sum::<&str, usize>::default();
        let expected = Sum(btreemap! {});
        assert_eq!(actual, expected);
    }
    #[test]
    fn from_entries() {
        let thb = "THB";
        let usd = "USD";
        let actual = sum!(100, thb; 200, usd);
        let expected = Sum(btreemap! {
            thb => 100,
            usd => 200,
        });
        assert_eq!(actual, expected);
    }
    #[test]
    fn set_amount_for_unit() {
        let unit = "USD";
        let mut actual = Sum::default();
        actual.set_amount_for_unit(3, unit);
        let expected = Sum(btreemap! { unit => 3 });
        assert_eq!(actual, expected);
    }
    #[test]
    fn unit_amount() {
        let unit = "USD";
        let amount = 123;
        let mut sum = Sum::default();
        sum.set_amount_for_unit(amount, unit);
        let actual = sum.unit_amount(&unit).unwrap();
        assert_eq!(actual, &amount);
    }
    #[test]
    fn amounts() {
        let thb = "THB";
        let usd = "USD";
        let sum = sum!(3, thb; 10, usd);
        let actual = sum.amounts().collect::<Vec<_>>();
        let expected = vec![(&thb, &3), (&usd, &10)];
        assert_eq!(actual, expected);
    }
    #[test]
    fn fmt_debug() {
        let usd = "USD";
        let amount_usd = 76;
        let thb = "THB";
        let amount_thb = 45;
        let sum = sum!(amount_usd, usd; amount_thb, thb);
        let actual = format!("{:?}", sum);
        let expected = format!(
            "Sum({{{:?}: {:?}, {:?}: {:?}}})",
            thb, amount_thb, usd, amount_usd
        );
        assert_eq!(actual, expected);
    }
}