anychain_core/
amount.rs

1use {
2    crate::no_std::{
3        fmt::{Debug, Display},
4        hash::Hash,
5        String,
6    },
7    thiserror::Error,
8};
9
10/// The interface for a generic amount.
11pub trait Amount:
12    Copy + Clone + Debug + Display + Send + Sync + 'static + Eq + Ord + Sized + Hash
13{
14}
15
16#[derive(Debug, Error)]
17pub enum AmountError {
18    #[error("{0}: {1}")]
19    Crate(&'static str, String),
20
21    #[error("the amount: {0:} exceeds the supply bounds of {1:}")]
22    AmountOutOfBounds(String, String),
23
24    #[error("invalid amount: {0:}")]
25    InvalidAmount(String),
26}
27
28/// Converts any available denomination to the minimum denomination
29pub fn to_basic_unit(value: &str, mut denomination: u32) -> String {
30    if denomination > 18 {
31        println!("illegal denomination");
32        return "".to_string();
33    }
34
35    let mut has_point: bool = false;
36    let mut point: usize = 0;
37    let mut cnt: usize = 0;
38
39    for c in value.chars() {
40        if c.is_ascii_digit() || c == '.' {
41            if c == '.' {
42                if has_point {
43                    println!("duplicate decimal point");
44                    return "".to_string();
45                }
46                if cnt == 0 {
47                    // the decimal point is at the front, indicating the value is 0
48                    return "0".to_string();
49                }
50                has_point = true;
51                point = cnt;
52            }
53            cnt += 1;
54        } else {
55            println!("illegal decimal string");
56            return "".to_string();
57        }
58    }
59
60    let mut value = value.to_string();
61
62    // the decimal string does not contain a decimal point,
63    // so we add one to the end.
64    if !has_point {
65        value.insert(value.len(), '.');
66        point = value.len() - 1;
67    }
68
69    let mut v = value.as_bytes().to_vec();
70
71    // now we right-shift the decimal point for 'denomination' times
72    while denomination > 0 {
73        // the decimal point is at the end of the vec, so push '0'
74        // to the end and swap it with the decimal point
75        if point == v.len() - 1 {
76            v.push(b'0');
77        }
78
79        // swap the decimal point with its next digit
80        v.swap(point, point + 1);
81
82        point += 1;
83        denomination -= 1;
84    }
85
86    // round up or down to the nearest integer
87    if point < v.len() - 1 && v[point + 1] > b'5' {
88        v[point - 1] += 1;
89    }
90
91    v.truncate(point);
92    String::from_utf8(v).unwrap()
93}
94
95/// Converts any available denomination to the minimum denomination
96pub fn to_basic_unit_u64(value: &str, mut denomination: u64) -> String {
97    if denomination > 18 {
98        println!("illegal denomination");
99        return "".to_string();
100    }
101
102    let mut has_point: bool = false;
103    let mut point: usize = 0;
104    let mut cnt: usize = 0;
105
106    for c in value.chars() {
107        if c.is_ascii_digit() || c == '.' {
108            if c == '.' {
109                if has_point {
110                    println!("duplicate decimal point");
111                    return "".to_string();
112                }
113                if cnt == 0 {
114                    // the decimal point is at the front, indicating the value is 0
115                    return "0".to_string();
116                }
117                has_point = true;
118                point = cnt;
119            }
120            cnt += 1;
121        } else {
122            println!("illegal decimal string");
123            return "".to_string();
124        }
125    }
126
127    let mut value = value.to_string();
128
129    // the decimal string does not contain a decimal point,
130    // so we add one to the end.
131    if !has_point {
132        value.insert(value.len(), '.');
133        point = value.len() - 1;
134    }
135
136    let mut v = value.as_bytes().to_vec();
137
138    // now we right-shift the decimal point for 'denomination' times
139    while denomination > 0 {
140        // the decimal point is at the end of the vec, so push '0'
141        // to the end and swap it with the decimal point
142        if point == v.len() - 1 {
143            v.push(b'0');
144        }
145
146        // swap the decimal point with its next digit
147        v.swap(point, point + 1);
148
149        point += 1;
150        denomination -= 1;
151    }
152
153    // round up or down to the nearest integer
154    if point < v.len() - 1 && v[point + 1] > b'5' {
155        v[point - 1] += 1;
156    }
157
158    v.truncate(point);
159    String::from_utf8(v).unwrap()
160}
161
162#[test]
163fn test() {
164    let s = to_basic_unit("0.0001037910", 7);
165    println!("s = {s}");
166}