1pub use defuse_nep245::TokenId;
2
3use std::{fmt, str::FromStr};
4
5use near_sdk::{AccountId, near};
6use serde_with::{DeserializeFromStr, SerializeDisplay};
7
8use crate::{TokenIdType, error::TokenIdError};
9
10#[cfg_attr(any(feature = "arbitrary", test), derive(::arbitrary::Arbitrary))]
12#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, SerializeDisplay, DeserializeFromStr)]
13#[near(serializers = [borsh])]
14pub struct ImtTokenId {
15 pub minter_id: AccountId,
16
17 pub token_id: TokenId,
18}
19
20impl ImtTokenId {
21 pub fn new(minter_id: impl Into<AccountId>, token_id: impl Into<TokenId>) -> Self {
22 Self {
23 minter_id: minter_id.into(),
24 token_id: token_id.into(),
25 }
26 }
27}
28
29impl std::fmt::Debug for ImtTokenId {
30 #[inline]
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 write!(f, "{}:{}", &self.minter_id, &self.token_id)
33 }
34}
35
36impl std::fmt::Display for ImtTokenId {
37 #[inline]
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 fmt::Debug::fmt(&self, f)
40 }
41}
42
43impl FromStr for ImtTokenId {
44 type Err = TokenIdError;
45
46 fn from_str(data: &str) -> Result<Self, Self::Err> {
47 let (minter_id, token_id) = data
48 .split_once(':')
49 .ok_or(strum::ParseError::VariantNotFound)?;
50 Ok(Self::new(minter_id.parse::<AccountId>()?, token_id))
51 }
52}
53
54impl From<&ImtTokenId> for TokenIdType {
55 #[inline]
56 fn from(_: &ImtTokenId) -> Self {
57 Self::Imt
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 use defuse_test_utils::random::make_arbitrary;
66 use rstest::rstest;
67
68 #[rstest]
69 #[trace]
70 fn display_from_str_roundtrip(#[from(make_arbitrary)] token_id: ImtTokenId) {
71 let s = token_id.to_string();
72 let got: ImtTokenId = s.parse().unwrap();
73 assert_eq!(got, token_id);
74 }
75}