psibase/
account_number.rs1use crate::{serialize_as_str, Pack, ToKey, ToSchema, Unpack};
2use custom_error::custom_error;
3use psibase_names::{account_number_from_str, account_number_to_string};
4use std::{num::ParseIntError, str::FromStr};
5
6custom_error! { pub AccountNumberError
7 Invalid{s:String} = "Invalid AccountNumber {s}",
8}
9
10#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Hash, Pack, Unpack, ToKey, ToSchema)]
25#[fracpack(
26 definition_will_not_change,
27 fracpack_mod = "fracpack",
28 custom = "AccountNumber"
29)]
30#[to_key(psibase_mod = "crate")]
31pub struct AccountNumber {
32 pub value: u64,
33}
34
35serialize_as_str!(AccountNumber, "account number");
36
37impl AccountNumber {
38 pub const fn new(value: u64) -> Self {
39 AccountNumber { value }
40 }
41
42 pub fn from_exact(s: &str) -> Result<Self, AccountNumberError> {
43 let result: Self = s.into();
44 if result.to_string() != s {
45 return Err(AccountNumberError::Invalid { s: s.into() });
46 }
47 Ok(result)
48 }
49}
50
51impl From<u64> for AccountNumber {
52 fn from(n: u64) -> Self {
53 AccountNumber { value: n }
54 }
55}
56
57impl From<ExactAccountNumber> for AccountNumber {
58 fn from(n: ExactAccountNumber) -> Self {
59 AccountNumber { value: n.value }
60 }
61}
62
63impl FromStr for AccountNumber {
64 type Err = ParseIntError;
65
66 fn from_str(s: &str) -> Result<Self, Self::Err> {
67 Ok(AccountNumber {
68 value: account_number_from_str(s),
69 })
70 }
71}
72
73impl From<&str> for AccountNumber {
74 fn from(s: &str) -> Self {
75 AccountNumber::from_str(s).unwrap()
76 }
77}
78
79impl std::fmt::Display for AccountNumber {
80 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
81 f.write_str(account_number_to_string(self.value).as_str())
82 }
83}
84
85#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Pack, Unpack, ToKey)]
87#[fracpack(
88 definition_will_not_change,
89 fracpack_mod = "fracpack",
90 custom = "AccountNumber"
91)]
92#[to_key(psibase_mod = "crate")]
93pub struct ExactAccountNumber {
94 pub value: u64,
95}
96
97serialize_as_str!(ExactAccountNumber, "account number");
98
99impl ExactAccountNumber {
100 pub fn new(value: u64) -> Self {
101 ExactAccountNumber { value }
102 }
103}
104
105impl From<u64> for ExactAccountNumber {
106 fn from(n: u64) -> Self {
107 ExactAccountNumber { value: n }
108 }
109}
110
111impl From<AccountNumber> for ExactAccountNumber {
112 fn from(n: AccountNumber) -> Self {
113 ExactAccountNumber { value: n.value }
114 }
115}
116
117impl FromStr for ExactAccountNumber {
118 type Err = AccountNumberError;
119
120 fn from_str(s: &str) -> Result<Self, Self::Err> {
121 Ok(ExactAccountNumber {
122 value: AccountNumber::from_exact(s)?.value,
123 })
124 }
125}
126
127impl std::fmt::Display for ExactAccountNumber {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 f.write_str(account_number_to_string(self.value).as_str())
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn empty_name_is_zero() {
139 assert_eq!(AccountNumber::from_str("").unwrap(), AccountNumber::new(0));
140 }
141
142 #[test]
143 fn name_longer_than_limit_of_18_is_zero() {
144 assert_eq!(
145 AccountNumber::from_str("abcdefghijklmnopqrstuvwxyz").unwrap(),
146 AccountNumber::new(0)
147 );
148 }
149
150 #[test]
151 fn name_starting_with_a_char_less_than_9_returns_zero() {
152 assert_eq!(AccountNumber::from_str("9").unwrap(), AccountNumber::new(0));
153 assert_eq!(AccountNumber::from_str("3").unwrap(), AccountNumber::new(0));
154 assert_eq!(
155 AccountNumber::from_str("1abc").unwrap(),
156 AccountNumber::new(0)
157 );
158 assert_eq!(
159 AccountNumber::from_str("1234").unwrap(),
160 AccountNumber::new(0)
161 );
162 assert_eq!(
163 AccountNumber::from_str("9asdf").unwrap(),
164 AccountNumber::new(0)
165 );
166 assert_ne!(
167 AccountNumber::from_str("abcd").unwrap(),
168 AccountNumber::new(0)
169 );
170 assert_ne!(
171 AccountNumber::from_str("asdf9").unwrap(),
172 AccountNumber::new(0)
173 );
174 assert_ne!(
175 AccountNumber::from_str("abc1").unwrap(),
176 AccountNumber::new(0)
177 );
178 }
179
180 #[test]
181 fn any_unknown_char_returns_zero() {
182 assert_eq!(AccountNumber::from_str("?").unwrap(), AccountNumber::new(0));
183 assert_eq!(
184 AccountNumber::from_str("what?").unwrap(),
185 AccountNumber::new(0)
186 );
187 assert_eq!(
188 AccountNumber::from_str("?what").unwrap(),
189 AccountNumber::new(0)
190 );
191 assert_eq!(
192 AccountNumber::from_str("eaorintsl?").unwrap(),
193 AccountNumber::new(0)
194 );
195 assert_eq!(
196 AccountNumber::from_str("????").unwrap(),
197 AccountNumber::new(0)
198 );
199 }
200
201 #[test]
202 fn returns_proper_numbers_from_str() {
203 assert_eq!(
204 AccountNumber::from_str("a").unwrap(),
205 AccountNumber::new(49158)
206 );
207 assert_eq!(
208 AccountNumber::from_str("b").unwrap(),
209 AccountNumber::new(184)
210 );
211 assert_eq!(
212 AccountNumber::from_str("c").unwrap(),
213 AccountNumber::new(16538)
214 );
215 assert_eq!(
216 AccountNumber::from_str("abc123").unwrap(),
217 AccountNumber::new(1754468116)
218 );
219 assert_eq!(
220 AccountNumber::from_str("spiderman").unwrap(),
221 AccountNumber::new(483466201442)
222 );
223 assert_eq!(
224 AccountNumber::from_str("brucewayne").unwrap(),
225 AccountNumber::new(132946582102463)
226 );
227 assert_eq!(
228 AccountNumber::from_str("anthonystark").unwrap(),
229 AccountNumber::new(183678712946955)
230 );
231 assert_eq!(
232 AccountNumber::from_str("natasharomanoff").unwrap(),
233 AccountNumber::new(5818245174062392369)
234 );
235 }
236
237 #[test]
238 fn name_number_value_to_string_is_converted_successfully() {
239 let name = AccountNumber::from_str("a").unwrap();
240 assert_eq!(name.value, 49158);
241 assert_eq!(name.to_string(), "a");
242
243 let name = AccountNumber::from_str("b").unwrap();
244 assert_eq!(name.to_string(), "b");
245 let name = AccountNumber::from(184);
246 assert_eq!(name.to_string(), "b");
247
248 let name = AccountNumber::from_str("c").unwrap();
249 assert_eq!(name.to_string(), "c");
250 let name = AccountNumber::from(16538);
251 assert_eq!(name.to_string(), "c");
252
253 let name = AccountNumber::from_str("abc123").unwrap();
254 assert_eq!(name.to_string(), "abc123");
255 let name = AccountNumber::from(1754468116);
256 assert_eq!(name.to_string(), "abc123");
257
258 let name = AccountNumber::from_str("spiderman").unwrap();
259 assert_eq!(name.to_string(), "spiderman");
260 let name = AccountNumber::from(483466201442);
261 assert_eq!(name.to_string(), "spiderman");
262
263 let name = AccountNumber::from(0);
264 assert_eq!(name.to_string(), "");
265 }
266}