fedimint_core/
tiered.rs

1use std::collections::BTreeMap;
2
3use fedimint_core::Amount;
4use serde::{Deserialize, Serialize};
5
6use crate::encoding::{Decodable, DecodeError, Encodable};
7use crate::module::registry::ModuleDecoderRegistry;
8
9#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize)]
10pub struct InvalidAmountTierError(pub Amount);
11
12impl std::fmt::Display for InvalidAmountTierError {
13    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
14        write!(f, "Amount tier unknown to mint: {}", self.0)
15    }
16}
17
18#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
19#[serde(transparent)]
20pub struct Tiered<T>(BTreeMap<Amount, T>);
21
22impl<T> Default for Tiered<T> {
23    fn default() -> Self {
24        Self(BTreeMap::default())
25    }
26}
27
28impl<T> Tiered<T> {
29    /// Returns the highest tier amount
30    pub fn max_tier(&self) -> &Amount {
31        self.0.keys().max().expect("has tiers")
32    }
33
34    pub fn structural_eq<O>(&self, other: &Tiered<O>) -> bool {
35        self.0.keys().eq(other.0.keys())
36    }
37
38    /// Returns a reference to the key of the specified tier
39    pub fn tier(&self, amount: &Amount) -> Result<&T, InvalidAmountTierError> {
40        self.0.get(amount).ok_or(InvalidAmountTierError(*amount))
41    }
42
43    pub fn count_tiers(&self) -> usize {
44        self.0.len()
45    }
46
47    pub fn tiers(&self) -> impl DoubleEndedIterator<Item = &Amount> {
48        self.0.keys()
49    }
50
51    pub fn values(&self) -> impl DoubleEndedIterator<Item = &T> {
52        self.0.values()
53    }
54
55    pub fn iter(&self) -> impl DoubleEndedIterator<Item = (Amount, &T)> {
56        self.0.iter().map(|(amt, key)| (*amt, key))
57    }
58
59    pub fn get(&self, amt: Amount) -> Option<&T> {
60        self.0.get(&amt)
61    }
62
63    pub fn get_mut(&mut self, amt: Amount) -> Option<&mut T> {
64        self.0.get_mut(&amt)
65    }
66
67    pub fn insert(&mut self, amt: Amount, v: T) -> Option<T> {
68        self.0.insert(amt, v)
69    }
70
71    pub fn get_mut_or_default(&mut self, amt: Amount) -> &mut T
72    where
73        T: Default,
74    {
75        self.0.entry(amt).or_default()
76    }
77
78    pub fn entry(&mut self, amt: Amount) -> std::collections::btree_map::Entry<'_, Amount, T>
79    where
80        T: Default,
81    {
82        self.0.entry(amt)
83    }
84
85    pub fn as_map(&self) -> &BTreeMap<Amount, T> {
86        &self.0
87    }
88}
89
90impl Tiered<()> {
91    /// Generates denominations of a given base up to and including `max`
92    pub fn gen_denominations(denomination_base: u16, max: Amount) -> Self {
93        let mut amounts = vec![];
94
95        let mut denomination = Amount::from_msats(1);
96        while denomination <= max {
97            amounts.push((denomination, ()));
98            denomination = denomination * denomination_base.into();
99        }
100
101        amounts.into_iter().collect()
102    }
103}
104
105impl<T> FromIterator<(Amount, T)> for Tiered<T> {
106    fn from_iter<I: IntoIterator<Item = (Amount, T)>>(iter: I) -> Self {
107        Self(iter.into_iter().collect())
108    }
109}
110
111impl<T> IntoIterator for Tiered<T> {
112    type Item = (Amount, T);
113    type IntoIter = std::collections::btree_map::IntoIter<Amount, T>;
114
115    fn into_iter(self) -> Self::IntoIter {
116        self.0.into_iter()
117    }
118}
119
120impl<C> Encodable for Tiered<C>
121where
122    C: Encodable,
123{
124    fn consensus_encode<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
125        self.0.consensus_encode(writer)?;
126        Ok(())
127    }
128}
129
130impl<C> Decodable for Tiered<C>
131where
132    C: Decodable,
133{
134    fn consensus_decode_partial<D: std::io::Read>(
135        d: &mut D,
136        modules: &ModuleDecoderRegistry,
137    ) -> Result<Self, DecodeError> {
138        Ok(Self(BTreeMap::consensus_decode_partial(d, modules)?))
139    }
140}
141
142#[cfg(test)]
143mod tests;