canic_cdk/types/
cycles.rs1use crate::{
2 candid::{CandidType, Nat},
3 structures::{Storable, storable::Bound},
4};
5use derive_more::{Add, AddAssign, Sub, SubAssign};
6use num_traits::cast::ToPrimitive;
7use serde::{Deserialize, Serialize, de::Deserializer};
8use std::{
9 borrow::Cow,
10 fmt::{self, Display},
11 str::FromStr,
12};
13
14pub const KC: u128 = 1_000;
20pub const MC: u128 = 1_000_000;
21pub const BC: u128 = 1_000_000_000;
22pub const TC: u128 = 1_000_000_000_000;
23pub const QC: u128 = 1_000_000_000_000_000;
24
25#[derive(
32 Add,
33 AddAssign,
34 CandidType,
35 Clone,
36 Default,
37 Debug,
38 Deserialize,
39 PartialEq,
40 Eq,
41 Hash,
42 PartialOrd,
43 Ord,
44 Serialize,
45 SubAssign,
46 Sub,
47)]
48pub struct Cycles(u128);
49
50impl Cycles {
51 #[must_use]
52 pub const fn new(n: u128) -> Self {
53 Self(n)
54 }
55
56 #[must_use]
57 pub fn to_u64(&self) -> u64 {
58 self.0.to_u64().unwrap_or(u64::MAX)
59 }
60
61 #[must_use]
62 pub const fn to_u128(&self) -> u128 {
63 self.0
64 }
65
66 pub fn from_config<'de, D>(deserializer: D) -> Result<Self, D::Error>
69 where
70 D: Deserializer<'de>,
71 {
72 #[derive(Deserialize)]
73 #[serde(untagged)]
74 enum Helper {
75 Str(String),
76 Num(u128),
77 }
78
79 match Helper::deserialize(deserializer)? {
80 Helper::Str(s) => s.parse::<Self>().map_err(serde::de::Error::custom),
81 Helper::Num(n) => Ok(Self::new(n)),
82 }
83 }
84}
85
86#[allow(clippy::cast_precision_loss)]
87impl Display for Cycles {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{:.3} TC", self.to_u128() as f64 / 1_000_000_000_000f64)
91 }
92}
93
94impl From<Nat> for Cycles {
95 fn from(n: Nat) -> Self {
96 Self(n.0.to_u128().unwrap_or(0))
97 }
98}
99
100impl From<u128> for Cycles {
101 fn from(v: u128) -> Self {
102 Self(v)
103 }
104}
105
106impl From<Cycles> for u128 {
107 fn from(c: Cycles) -> Self {
108 c.0
109 }
110}
111
112#[allow(clippy::cast_precision_loss)]
114#[allow(clippy::cast_sign_loss)]
115#[allow(clippy::cast_possible_truncation)]
116impl FromStr for Cycles {
117 type Err = String;
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 let mut num = String::new();
120 let mut suf = String::new();
121 let mut suf_count = 0;
122 for c in s.chars() {
123 if c.is_ascii_digit() || c == '.' {
124 if suf_count > 0 {
125 return Err("invalid suffix".to_string());
126 }
127 num.push(c);
128 } else if suf_count >= 2 {
129 return Err("invalid suffix".to_string());
130 } else {
131 suf.push(c);
132 suf_count += 1;
133 }
134 }
135
136 let mut n = num.parse::<f64>().map_err(|e| e.to_string())?;
137 match suf.as_str() {
138 "" => {}
139 "K" => n *= KC as f64,
140 "M" => n *= MC as f64,
141 "B" => n *= BC as f64,
142 "T" => n *= TC as f64,
143 "Q" => n *= QC as f64,
144 _ => return Err("invalid suffix".to_string()),
145 }
146
147 Ok(Self::new(n as u128))
148 }
149}
150
151impl Storable for Cycles {
152 const BOUND: Bound = Bound::Bounded {
154 max_size: 16,
155 is_fixed_size: true,
156 };
157
158 fn to_bytes(&self) -> Cow<'_, [u8]> {
159 Cow::Owned(self.0.to_be_bytes().to_vec())
160 }
161
162 fn into_bytes(self) -> Vec<u8> {
163 self.0.to_be_bytes().to_vec()
164 }
165
166 fn from_bytes(bytes: Cow<[u8]>) -> Self {
167 let b = bytes.as_ref();
168
169 if b.len() != 16 {
171 return Self::default();
172 }
173
174 let mut arr = [0u8; 16];
175 arr.copy_from_slice(b);
176
177 Self(u128::from_be_bytes(arr))
178 }
179}