dubp_wallet/
source.rs

1//  Copyright (C) 2020  Éloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Define DUBP currency source.
17
18pub mod v10;
19
20use crate::*;
21use std::cmp::Ordering;
22
23/// Wrap a source amount
24#[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        /*assert_eq!(
158            SourceAmount::new(36, 1),
159            sa3 + sa2,
160        );*/
161    }
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        //let sa3 = SourceAmount::new(123, 0);
170
171        assert_eq!(SourceAmount::new(24, 1), sa2 - sa1,);
172        /*assert_eq!(
173            SourceAmount::new(24, 1),
174            sa2 - sa3,
175        );c
176        assert_eq!(SourceAmount::new(0, 1), sa3 - sa1,);*/
177    }
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}