ckb_sdk/types/
human_capacity.rs

1use std::fmt;
2use std::ops::Deref;
3use std::str::FromStr;
4
5use crate::constants::ONE_CKB;
6
7#[derive(Default, Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
8pub struct HumanCapacity(pub u64);
9
10impl From<u64> for HumanCapacity {
11    fn from(value: u64) -> HumanCapacity {
12        HumanCapacity(value)
13    }
14}
15
16impl From<HumanCapacity> for u64 {
17    fn from(value: HumanCapacity) -> u64 {
18        value.0
19    }
20}
21
22impl Deref for HumanCapacity {
23    type Target = u64;
24    fn deref(&self) -> &u64 {
25        &self.0
26    }
27}
28
29impl FromStr for HumanCapacity {
30    type Err = String;
31    fn from_str(input: &str) -> Result<Self, Self::Err> {
32        let parts = input.trim().split('.').collect::<Vec<_>>();
33        let mut capacity = ONE_CKB
34            * parts
35                .first()
36                .ok_or_else(|| "Missing input".to_owned())?
37                .parse::<u64>()
38                .map_err(|err| err.to_string())?;
39        if let Some(shannon_str) = parts.get(1) {
40            let shannon_str = shannon_str.trim();
41            if shannon_str.len() > 8 {
42                return Err(format!("decimal part too long: {}", shannon_str.len()));
43            }
44            let mut shannon = shannon_str.parse::<u32>().map_err(|err| err.to_string())?;
45            for _ in 0..(8 - shannon_str.len()) {
46                shannon *= 10;
47            }
48            capacity += u64::from(shannon);
49        }
50        Ok(capacity.into())
51    }
52}
53
54impl fmt::Display for HumanCapacity {
55    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
56        let ckb_part = self.0 / ONE_CKB;
57        let shannon_part = self.0 % ONE_CKB;
58        let shannon_part_string = format!("{:0>8}", shannon_part);
59        let mut base = 10;
60        let mut suffix_zero = 7;
61        for i in 0..8 {
62            if shannon_part % base > 0 {
63                suffix_zero = i;
64                break;
65            }
66            base *= 10;
67        }
68        if f.alternate() {
69            write!(
70                f,
71                "{}.{} (CKB)",
72                ckb_part,
73                &shannon_part_string[..(8 - suffix_zero)]
74            )
75        } else {
76            write!(
77                f,
78                "{}.{}",
79                ckb_part,
80                &shannon_part_string[..(8 - suffix_zero)]
81            )
82        }
83    }
84}
85
86#[cfg(test)]
87mod test {
88    use super::*;
89
90    #[test]
91    fn test_human_capacity() {
92        for (input, capacity) in &[
93            ("3.0", 3 * ONE_CKB),
94            ("300.0", 300 * ONE_CKB),
95            ("3.56", 356_000_000),
96            ("3.0056", 300_560_000),
97            ("3.10056", 310_056_000),
98            ("3.10056123", 310_056_123),
99            ("0.0056", 560_000),
100            ("0.10056123", 10_056_123),
101            ("12345.234", 12345 * ONE_CKB + 23_400_000),
102            ("12345.23442222", 12345 * ONE_CKB + 23_442_222),
103        ] {
104            assert_eq!(HumanCapacity::from_str(input).unwrap(), (*capacity).into());
105            assert_eq!(HumanCapacity::from(*capacity).to_string(), *input);
106        }
107
108        // Parse capacity without decimal part
109        assert_eq!(
110            HumanCapacity::from_str("12345"),
111            Ok(HumanCapacity::from(12345 * ONE_CKB))
112        );
113
114        // Parse capacity failed
115        assert!(HumanCapacity::from_str("12345.234422224").is_err());
116        assert!(HumanCapacity::from_str("abc.234422224").is_err());
117        assert!(HumanCapacity::from_str("abc.abc").is_err());
118        assert!(HumanCapacity::from_str("abc").is_err());
119        assert!(HumanCapacity::from_str("-234").is_err());
120        assert!(HumanCapacity::from_str("-234.3").is_err());
121    }
122}