1use canic_cdk::{candid::CandidType, types::Nat};
9use derive_more::{Add, AddAssign, Sub, SubAssign};
10use num_traits::cast::ToPrimitive;
11use serde::{Deserialize, Serialize, de::Deserializer};
12use std::{
13 fmt::{self, Display},
14 str::FromStr,
15};
16
17pub const KC: u128 = 1_000;
23pub const MC: u128 = 1_000_000;
24pub const BC: u128 = 1_000_000_000;
25pub const TC: u128 = 1_000_000_000_000;
26pub const QC: u128 = 1_000_000_000_000_000;
27
28#[derive(
35 Add,
36 AddAssign,
37 CandidType,
38 Clone,
39 Default,
40 Debug,
41 Deserialize,
42 PartialEq,
43 Eq,
44 Hash,
45 PartialOrd,
46 Ord,
47 Serialize,
48 SubAssign,
49 Sub,
50)]
51pub struct Cycles(Nat);
52
53impl Cycles {
54 #[must_use]
55 pub fn new(amount: u128) -> Self {
56 Self(amount.into())
57 }
58
59 #[must_use]
60 pub fn to_u64(&self) -> u64 {
61 self.0.0.to_u64().unwrap_or(u64::MAX)
62 }
63
64 #[must_use]
65 pub fn to_u128(&self) -> u128 {
66 self.0.0.to_u128().unwrap_or(u128::MAX)
67 }
68
69 pub fn from_config<'de, D>(deserializer: D) -> Result<Self, D::Error>
72 where
73 D: Deserializer<'de>,
74 {
75 #[derive(Deserialize)]
76 #[serde(untagged)]
77 enum Helper {
78 Str(String),
79 Num(u128),
80 }
81
82 match Helper::deserialize(deserializer)? {
83 Helper::Str(s) => s.parse::<Self>().map_err(serde::de::Error::custom),
84 Helper::Num(n) => Ok(Self::new(n)),
85 }
86 }
87}
88
89#[allow(clippy::cast_precision_loss)]
90impl Display for Cycles {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "{:.3} TC", self.to_u128() as f64 / 1_000_000_000_000f64)
94 }
95}
96
97impl From<u128> for Cycles {
98 fn from(v: u128) -> Self {
99 Self(Nat::from(v))
100 }
101}
102
103impl From<Nat> for Cycles {
104 fn from(n: Nat) -> Self {
105 Self(n)
106 }
107}
108
109impl From<Cycles> for Nat {
110 fn from(c: Cycles) -> Self {
111 c.0
112 }
113}
114
115#[allow(clippy::cast_precision_loss)]
117#[allow(clippy::cast_sign_loss)]
118#[allow(clippy::cast_possible_truncation)]
119impl FromStr for Cycles {
120 type Err = String;
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 let mut num = String::new();
123 let mut suf = String::new();
124 let mut suf_count = 0;
125 for c in s.chars() {
126 if c.is_ascii_digit() || c == '.' {
127 if suf_count > 0 {
128 return Err("invalid suffix".to_string());
129 }
130 num.push(c);
131 } else if suf_count >= 2 {
132 return Err("invalid suffix".to_string());
133 } else {
134 suf.push(c);
135 suf_count += 1;
136 }
137 }
138
139 let mut n = num.parse::<f64>().map_err(|e| e.to_string())?;
140 match suf.as_str() {
141 "" => {}
142 "K" => n *= KC as f64,
143 "M" => n *= MC as f64,
144 "B" => n *= BC as f64,
145 "T" => n *= TC as f64,
146 "Q" => n *= QC as f64,
147 _ => return Err("invalid suffix".to_string()),
148 }
149
150 Ok(Self::new(n as u128))
151 }
152}