unc_account_id/
account_id.rs1use std::{borrow::Cow, fmt, ops::Deref, str::FromStr};
2
3use crate::{AccountIdRef, ParseAccountError};
4
5#[derive(Eq, Ord, Hash, Clone, Debug, PartialEq, PartialOrd)]
23#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
24#[cfg_attr(feature = "abi", derive(borsh::BorshSchema))]
25pub struct AccountId(pub(crate) Box<str>);
26
27impl AccountId {
28 pub const MIN_LEN: usize = crate::validation::MIN_LEN;
30 pub const MAX_LEN: usize = crate::validation::MAX_LEN;
32
33 #[doc(hidden)]
53 #[cfg(feature = "internal_unstable")]
54 #[deprecated = "AccountId construction without validation is illegal"]
55 pub fn new_unvalidated(account_id: String) -> Self {
56 Self(account_id.into_boxed_str())
57 }
58
59 pub fn validate(account_id: &str) -> Result<(), ParseAccountError> {
116 crate::validation::validate(account_id)
117 }
118}
119
120impl AsRef<str> for AccountId {
121 fn as_ref(&self) -> &str {
122 &self.0
123 }
124}
125
126impl AsRef<AccountIdRef> for AccountId {
127 fn as_ref(&self) -> &AccountIdRef {
128 self
129 }
130}
131
132impl Deref for AccountId {
133 type Target = AccountIdRef;
134
135 fn deref(&self) -> &Self::Target {
136 AccountIdRef::new_unvalidated(&self.0)
137 }
138}
139
140impl std::borrow::Borrow<AccountIdRef> for AccountId {
141 fn borrow(&self) -> &AccountIdRef {
142 AccountIdRef::new_unvalidated(self)
143 }
144}
145
146impl FromStr for AccountId {
147 type Err = ParseAccountError;
148
149 fn from_str(account_id: &str) -> Result<Self, Self::Err> {
150 crate::validation::validate(account_id)?;
151 Ok(Self(account_id.into()))
152 }
153}
154
155impl TryFrom<Box<str>> for AccountId {
156 type Error = ParseAccountError;
157
158 fn try_from(account_id: Box<str>) -> Result<Self, Self::Error> {
159 crate::validation::validate(&account_id)?;
160 Ok(Self(account_id))
161 }
162}
163
164impl TryFrom<String> for AccountId {
165 type Error = ParseAccountError;
166
167 fn try_from(account_id: String) -> Result<Self, Self::Error> {
168 crate::validation::validate(&account_id)?;
169 Ok(Self(account_id.into_boxed_str()))
170 }
171}
172
173impl fmt::Display for AccountId {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 fmt::Display::fmt(&self.0, f)
176 }
177}
178
179impl From<AccountId> for String {
180 fn from(account_id: AccountId) -> Self {
181 account_id.0.into_string()
182 }
183}
184
185impl From<AccountId> for Box<str> {
186 fn from(value: AccountId) -> Box<str> {
187 value.0
188 }
189}
190
191impl PartialEq<AccountId> for AccountIdRef {
192 fn eq(&self, other: &AccountId) -> bool {
193 &self.0 == other.as_str()
194 }
195}
196
197impl PartialEq<AccountIdRef> for AccountId {
198 fn eq(&self, other: &AccountIdRef) -> bool {
199 self.as_str() == &other.0
200 }
201}
202
203impl<'a> PartialEq<AccountId> for &'a AccountIdRef {
204 fn eq(&self, other: &AccountId) -> bool {
205 &self.0 == other.as_str()
206 }
207}
208
209impl<'a> PartialEq<&'a AccountIdRef> for AccountId {
210 fn eq(&self, other: &&'a AccountIdRef) -> bool {
211 self.as_str() == &other.0
212 }
213}
214
215impl PartialEq<AccountId> for String {
216 fn eq(&self, other: &AccountId) -> bool {
217 self == other.as_str()
218 }
219}
220
221impl PartialEq<String> for AccountId {
222 fn eq(&self, other: &String) -> bool {
223 self.as_str() == other
224 }
225}
226
227impl PartialEq<AccountId> for str {
228 fn eq(&self, other: &AccountId) -> bool {
229 self == other.as_str()
230 }
231}
232
233impl PartialEq<str> for AccountId {
234 fn eq(&self, other: &str) -> bool {
235 self.as_str() == other
236 }
237}
238
239impl<'a> PartialEq<AccountId> for &'a str {
240 fn eq(&self, other: &AccountId) -> bool {
241 *self == other.as_str()
242 }
243}
244
245impl<'a> PartialEq<&'a str> for AccountId {
246 fn eq(&self, other: &&'a str) -> bool {
247 self.as_str() == *other
248 }
249}
250
251impl PartialOrd<AccountId> for AccountIdRef {
252 fn partial_cmp(&self, other: &AccountId) -> Option<std::cmp::Ordering> {
253 self.0.partial_cmp(other.as_str())
254 }
255}
256
257impl PartialOrd<AccountIdRef> for AccountId {
258 fn partial_cmp(&self, other: &AccountIdRef) -> Option<std::cmp::Ordering> {
259 self.as_str().partial_cmp(&other.0)
260 }
261}
262
263impl<'a> PartialOrd<AccountId> for &'a AccountIdRef {
264 fn partial_cmp(&self, other: &AccountId) -> Option<std::cmp::Ordering> {
265 self.0.partial_cmp(other.as_str())
266 }
267}
268
269impl<'a> PartialOrd<&'a AccountIdRef> for AccountId {
270 fn partial_cmp(&self, other: &&'a AccountIdRef) -> Option<std::cmp::Ordering> {
271 self.as_str().partial_cmp(&other.0)
272 }
273}
274
275impl PartialOrd<AccountId> for String {
276 fn partial_cmp(&self, other: &AccountId) -> Option<std::cmp::Ordering> {
277 self.as_str().partial_cmp(other.as_str())
278 }
279}
280
281impl PartialOrd<String> for AccountId {
282 fn partial_cmp(&self, other: &String) -> Option<std::cmp::Ordering> {
283 self.as_str().partial_cmp(other.as_str())
284 }
285}
286
287impl PartialOrd<AccountId> for str {
288 fn partial_cmp(&self, other: &AccountId) -> Option<std::cmp::Ordering> {
289 self.partial_cmp(other.as_str())
290 }
291}
292
293impl PartialOrd<str> for AccountId {
294 fn partial_cmp(&self, other: &str) -> Option<std::cmp::Ordering> {
295 self.as_str().partial_cmp(other)
296 }
297}
298
299impl<'a> PartialOrd<AccountId> for &'a str {
300 fn partial_cmp(&self, other: &AccountId) -> Option<std::cmp::Ordering> {
301 self.partial_cmp(&other.as_str())
302 }
303}
304
305impl<'a> PartialOrd<&'a str> for AccountId {
306 fn partial_cmp(&self, other: &&'a str) -> Option<std::cmp::Ordering> {
307 self.as_str().partial_cmp(*other)
308 }
309}
310
311impl<'a> From<AccountId> for Cow<'a, AccountIdRef> {
312 fn from(value: AccountId) -> Self {
313 Cow::Owned(value)
314 }
315}
316
317impl<'a> From<&'a AccountId> for Cow<'a, AccountIdRef> {
318 fn from(value: &'a AccountId) -> Self {
319 Cow::Borrowed(value)
320 }
321}
322
323impl<'a> From<Cow<'a, AccountIdRef>> for AccountId {
324 fn from(value: Cow<'a, AccountIdRef>) -> Self {
325 value.into_owned()
326 }
327}
328
329#[cfg(feature = "arbitrary")]
330impl<'a> arbitrary::Arbitrary<'a> for AccountId {
331 fn size_hint(depth: usize) -> (usize, Option<usize>) {
332 <&AccountIdRef as arbitrary::Arbitrary>::size_hint(depth)
333 }
334
335 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
336 Ok(u.arbitrary::<&AccountIdRef>()?.into())
337 }
338
339 fn arbitrary_take_rest(u: arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
340 Ok(<&AccountIdRef as arbitrary::Arbitrary>::arbitrary_take_rest(u)?.into())
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 #[allow(unused_imports)]
347 use super::*;
348
349 #[test]
350 #[cfg(feature = "arbitrary")]
351 fn test_arbitrary() {
352 let corpus = [
353 ("a|bcd", None),
354 ("ab|cde", Some("ab")),
355 ("a_-b", None),
356 ("ab_-c", Some("ab")),
357 ("a", None),
358 ("miraclx.unc", Some("miraclx.unc")),
359 (
360 "01234567890123456789012345678901234567890123456789012345678901234",
361 None,
362 ),
363 ];
364
365 for (input, expected_output) in corpus {
366 assert!(input.len() <= u8::MAX as usize);
367 let data = [input.as_bytes(), &[input.len() as _]].concat();
368 let mut u = arbitrary::Unstructured::new(&data);
369
370 assert_eq!(
371 u.arbitrary::<AccountId>().map(Into::<String>::into).ok(),
372 expected_output.map(Into::<String>::into)
373 );
374 }
375 }
376 #[test]
377 #[cfg(feature = "schemars")]
378 fn test_schemars() {
379 let schema = schemars::schema_for!(AccountId);
380 let json_schema = serde_json::to_value(&schema).unwrap();
381 dbg!(&json_schema);
382 assert_eq!(
383 json_schema,
384 serde_json::json!({
385 "$schema": "http://json-schema.org/draft-07/schema#",
386 "description": "Utility Account Identifier.\n\nThis is a unique, syntactically valid, human-readable account identifier on the Utility network.\n\n[See the crate-level docs for information about validation.](index.html#account-id-rules)\n\nAlso see [Error kind precedence](AccountId#error-kind-precedence).\n\n## Examples\n\n``` use unc_account_id::AccountId;\n\nlet alice: AccountId = \"alice.unc\".parse().unwrap();\n\nassert!(\"ƒelicia.unc\".parse::<AccountId>().is_err()); // (ƒ is not f) ```",
387 "title": "AccountId",
388 "type": "string"
389 }
390 )
391 );
392 }
393}