1use core::{
4 fmt,
5 iter::Sum,
6 ops::{Add, AddAssign, Div, Mul, Sub},
7};
8
9#[cfg(feature = "datasize")]
10use datasize::DataSize;
11use num::Zero;
12use serde::{Deserialize, Serialize};
13
14use crate::{Motes, U512};
15
16#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
18#[cfg_attr(feature = "datasize", derive(DataSize))]
19pub struct Gas(U512);
20
21impl Gas {
22 pub fn new(value: U512) -> Self {
24 Gas(value)
25 }
26
27 pub fn value(&self) -> U512 {
29 self.0
30 }
31
32 pub fn from_motes(motes: Motes, conv_rate: u64) -> Option<Self> {
36 motes
37 .value()
38 .checked_div(U512::from(conv_rate))
39 .map(Self::new)
40 }
41
42 pub fn checked_add(&self, rhs: Self) -> Option<Self> {
44 self.0.checked_add(rhs.value()).map(Self::new)
45 }
46
47 pub fn checked_sub(&self, rhs: Self) -> Option<Self> {
49 self.0.checked_sub(rhs.value()).map(Self::new)
50 }
51}
52
53impl fmt::Display for Gas {
54 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
55 write!(f, "{:?}", self.0)
56 }
57}
58
59impl Add for Gas {
60 type Output = Gas;
61
62 fn add(self, rhs: Self) -> Self::Output {
63 let val = self.value() + rhs.value();
64 Gas::new(val)
65 }
66}
67
68impl Sub for Gas {
69 type Output = Gas;
70
71 fn sub(self, rhs: Self) -> Self::Output {
72 let val = self.value() - rhs.value();
73 Gas::new(val)
74 }
75}
76
77impl Div for Gas {
78 type Output = Gas;
79
80 fn div(self, rhs: Self) -> Self::Output {
81 let val = self.value() / rhs.value();
82 Gas::new(val)
83 }
84}
85
86impl Mul for Gas {
87 type Output = Gas;
88
89 fn mul(self, rhs: Self) -> Self::Output {
90 let val = self.value() * rhs.value();
91 Gas::new(val)
92 }
93}
94
95impl AddAssign for Gas {
96 fn add_assign(&mut self, rhs: Self) {
97 self.0 += rhs.0
98 }
99}
100
101impl Zero for Gas {
102 fn zero() -> Self {
103 Gas::new(U512::zero())
104 }
105
106 fn is_zero(&self) -> bool {
107 self.0.is_zero()
108 }
109}
110
111impl Sum for Gas {
112 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
113 iter.fold(Gas::zero(), Add::add)
114 }
115}
116
117impl From<u32> for Gas {
118 fn from(gas: u32) -> Self {
119 let gas_u512: U512 = gas.into();
120 Gas::new(gas_u512)
121 }
122}
123
124impl From<u64> for Gas {
125 fn from(gas: u64) -> Self {
126 let gas_u512: U512 = gas.into();
127 Gas::new(gas_u512)
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use crate::U512;
134
135 use crate::{Gas, Motes};
136
137 #[test]
138 fn should_be_able_to_get_instance_of_gas() {
139 let initial_value = 1;
140 let gas = Gas::new(U512::from(initial_value));
141 assert_eq!(
142 initial_value,
143 gas.value().as_u64(),
144 "should have equal value"
145 )
146 }
147
148 #[test]
149 fn should_be_able_to_compare_two_instances_of_gas() {
150 let left_gas = Gas::new(U512::from(1));
151 let right_gas = Gas::new(U512::from(1));
152 assert_eq!(left_gas, right_gas, "should be equal");
153 let right_gas = Gas::new(U512::from(2));
154 assert_ne!(left_gas, right_gas, "should not be equal")
155 }
156
157 #[test]
158 fn should_be_able_to_add_two_instances_of_gas() {
159 let left_gas = Gas::new(U512::from(1));
160 let right_gas = Gas::new(U512::from(1));
161 let expected_gas = Gas::new(U512::from(2));
162 assert_eq!((left_gas + right_gas), expected_gas, "should be equal")
163 }
164
165 #[test]
166 fn should_be_able_to_subtract_two_instances_of_gas() {
167 let left_gas = Gas::new(U512::from(1));
168 let right_gas = Gas::new(U512::from(1));
169 let expected_gas = Gas::new(U512::from(0));
170 assert_eq!((left_gas - right_gas), expected_gas, "should be equal")
171 }
172
173 #[test]
174 fn should_be_able_to_multiply_two_instances_of_gas() {
175 let left_gas = Gas::new(U512::from(100));
176 let right_gas = Gas::new(U512::from(10));
177 let expected_gas = Gas::new(U512::from(1000));
178 assert_eq!((left_gas * right_gas), expected_gas, "should be equal")
179 }
180
181 #[test]
182 fn should_be_able_to_divide_two_instances_of_gas() {
183 let left_gas = Gas::new(U512::from(1000));
184 let right_gas = Gas::new(U512::from(100));
185 let expected_gas = Gas::new(U512::from(10));
186 assert_eq!((left_gas / right_gas), expected_gas, "should be equal")
187 }
188
189 #[test]
190 fn should_be_able_to_convert_from_mote() {
191 let mote = Motes::new(U512::from(100));
192 let gas = Gas::from_motes(mote, 10).expect("should have gas");
193 let expected_gas = Gas::new(U512::from(10));
194 assert_eq!(gas, expected_gas, "should be equal")
195 }
196
197 #[test]
198 fn should_be_able_to_default() {
199 let gas = Gas::default();
200 let expected_gas = Gas::new(U512::from(0));
201 assert_eq!(gas, expected_gas, "should be equal")
202 }
203
204 #[test]
205 fn should_be_able_to_compare_relative_value() {
206 let left_gas = Gas::new(U512::from(100));
207 let right_gas = Gas::new(U512::from(10));
208 assert!(left_gas > right_gas, "should be gt");
209 let right_gas = Gas::new(U512::from(100));
210 assert!(left_gas >= right_gas, "should be gte");
211 assert!(left_gas <= right_gas, "should be lte");
212 let left_gas = Gas::new(U512::from(10));
213 assert!(left_gas < right_gas, "should be lt");
214 }
215
216 #[test]
217 fn should_default() {
218 let left_gas = Gas::new(U512::from(0));
219 let right_gas = Gas::default();
220 assert_eq!(left_gas, right_gas, "should be equal");
221 let u512 = U512::zero();
222 assert_eq!(left_gas.value(), u512, "should be equal");
223 }
224
225 #[test]
226 fn should_support_checked_div_from_motes() {
227 let motes = Motes::new(U512::zero());
228 let conv_rate = 0;
229 let maybe = Gas::from_motes(motes, conv_rate);
230 assert!(maybe.is_none(), "should be none due to divide by zero");
231 }
232}