1pub mod v10;
19
20use crate::*;
21use std::cmp::Ordering;
22
23#[derive(
25 Debug, Default, Copy, Clone, Eq, Deserialize, Serialize, zerocopy::AsBytes, zerocopy::FromBytes,
26)]
27#[repr(transparent)]
28pub struct SourceAmount([u8; 16]);
29
30impl std::fmt::Display for SourceAmount {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 write!(f, "{}:{}", self.amount(), self.base())
33 }
34}
35
36impl SourceAmount {
37 pub const ZERO: Self = SourceAmount([0u8; 16]);
38
39 pub fn new(amount: i64, base: i64) -> Self {
40 let mut buffer = [0; 16];
41 buffer[..8].copy_from_slice(&amount.to_be_bytes()[..]);
42 buffer[8..].copy_from_slice(&base.to_be_bytes()[..]);
43 Self(buffer)
44 }
45 pub fn amount(&self) -> i64 {
46 zerocopy::LayoutVerified::<_, zerocopy::I64<byteorder::BigEndian>>::new(&self.0[..8])
47 .unwrap_or_else(|| unreachable!())
48 .get()
49 }
50 pub fn base(&self) -> i64 {
51 zerocopy::LayoutVerified::<_, zerocopy::I64<byteorder::BigEndian>>::new(&self.0[8..])
52 .unwrap_or_else(|| unreachable!())
53 .get()
54 }
55 pub fn with_base0(amount: i64) -> Self {
56 let mut buffer = [0; 16];
57 buffer[..8].copy_from_slice(&amount.to_be_bytes()[..]);
58 Self(buffer)
59 }
60 pub fn increment_base(self) -> Self {
61 Self::new(self.amount() / 10, self.base() + 1)
62 }
63}
64
65impl PartialEq for SourceAmount {
66 #[allow(clippy::comparison_chain)]
67 fn eq(&self, other: &Self) -> bool {
68 if self.base() == other.base() {
69 self.amount().eq(&other.amount())
70 } else if self.base() > other.base() {
71 self.eq(&(*other).increment_base())
72 } else {
73 self.increment_base().eq(other)
74 }
75 }
76}
77
78impl PartialOrd for SourceAmount {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 #[allow(clippy::comparison_chain)]
81 if self.base() == other.base() {
82 self.amount().partial_cmp(&other.amount())
83 } else if self.base() > other.base() {
84 self.partial_cmp(&other.increment_base())
85 } else {
86 self.increment_base().partial_cmp(other)
87 }
88 }
89}
90
91impl Ord for SourceAmount {
92 fn cmp(&self, other: &Self) -> Ordering {
93 #[allow(clippy::comparison_chain)]
94 if self.base() == other.base() {
95 self.amount().cmp(&other.amount())
96 } else if self.base() > other.base() {
97 self.cmp(&other.increment_base())
98 } else {
99 self.increment_base().cmp(other)
100 }
101 }
102}
103
104impl Add for SourceAmount {
105 type Output = SourceAmount;
106
107 #[allow(clippy::comparison_chain)]
108 fn add(self, a: SourceAmount) -> Self::Output {
109 if self.base() == a.base() {
110 SourceAmount::new(self.amount() + a.amount(), self.base())
111 } else if self.base() > a.base() {
112 self.add(a.increment_base())
113 } else {
114 self.increment_base().add(a)
115 }
116 }
117}
118impl Sub for SourceAmount {
119 type Output = SourceAmount;
120
121 #[allow(clippy::comparison_chain)]
122 fn sub(self, a: SourceAmount) -> Self::Output {
123 if self.base() == a.base() {
124 SourceAmount::new(self.amount() - a.amount(), self.base())
125 } else if self.base() > a.base() {
126 self.sub(a.increment_base())
127 } else {
128 self.increment_base().sub(a)
129 }
130 }
131}
132
133impl Sum for SourceAmount {
134 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
135 iter.fold(SourceAmount::with_base0(0), Add::add)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_zero() {
145 assert_eq!(SourceAmount::ZERO.amount(), 0);
146 assert_eq!(SourceAmount::ZERO.base(), 0);
147 }
148
149 #[test]
150 fn test_add_sources_amount() {
151 let sa1 = SourceAmount::new(12, 1);
152 let sa2 = SourceAmount::new(24, 1);
153 let sa3 = SourceAmount::new(123, 0);
154
155 assert_eq!(SourceAmount::new(36, 1), sa1 + sa2,);
156 assert_eq!(SourceAmount::new(36, 1), sa2 + sa3,);
157 }
162
163 #[test]
164 fn test_sub_sources_amount() {
165 let sa1 = SourceAmount::new(12, 1);
166 assert_eq!(sa1.amount(), 12);
167 assert_eq!(sa1.base(), 1);
168 let sa2 = SourceAmount::new(36, 1);
169 assert_eq!(SourceAmount::new(24, 1), sa2 - sa1,);
172 }
178
179 #[test]
180 fn test_ord() {
181 let a1 = SourceAmount::new(20, 3);
182 let a2 = SourceAmount::new(9_000, 0);
183
184 assert!(a1 > a2);
185 }
186}