use std::fmt::{
self,
Debug,
Display,
Formatter,
};
use std::str::FromStr;
use hiero_sdk_proto::services;
use crate::entity_id::{
Checksum,
ValidateChecksums,
};
use crate::{
Client,
EntityId,
Error,
FromProtobuf,
NftId,
ToProtobuf,
};
#[derive(Hash, PartialEq, Eq, Clone, Copy)]
pub struct TokenId {
pub shard: u64,
pub realm: u64,
pub num: u64,
pub checksum: Option<Checksum>,
}
impl TokenId {
#[must_use]
pub const fn new(shard: u64, realm: u64, num: u64) -> Self {
Self { shard, realm, num, checksum: None }
}
pub fn from_bytes(bytes: &[u8]) -> crate::Result<Self> {
FromProtobuf::from_bytes(bytes)
}
pub fn from_solidity_address(address: &str) -> crate::Result<Self> {
let EntityId { shard, realm, num, checksum } = EntityId::from_solidity_address(address)?;
Ok(Self { shard, realm, num, checksum })
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
ToProtobuf::to_bytes(self)
}
pub fn to_solidity_address(&self) -> crate::Result<String> {
EntityId { shard: self.shard, realm: self.realm, num: self.num, checksum: None }
.to_solidity_address()
}
#[must_use]
pub fn to_string_with_checksum(&self, client: &Client) -> String {
EntityId::to_string_with_checksum(self.to_string(), client)
}
pub fn validate_checksum(&self, client: &Client) -> crate::Result<()> {
EntityId::validate_checksum(self.shard, self.realm, self.num, self.checksum, client)
}
#[must_use]
pub fn nft(&self, serial: u64) -> NftId {
NftId { token_id: *self, serial }
}
}
impl ValidateChecksums for TokenId {
fn validate_checksums(&self, ledger_id: &crate::ledger_id::RefLedgerId) -> Result<(), Error> {
EntityId::validate_checksum_for_ledger_id(
self.shard,
self.realm,
self.num,
self.checksum,
ledger_id,
)
}
}
impl Debug for TokenId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "\"{self}\"")
}
}
impl Display for TokenId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}.{}", self.shard, self.realm, self.num)
}
}
impl FromProtobuf<services::TokenId> for TokenId {
fn from_protobuf(pb: services::TokenId) -> crate::Result<Self> {
Ok(Self {
num: pb.token_num as u64,
shard: pb.shard_num as u64,
realm: pb.realm_num as u64,
checksum: None,
})
}
}
impl ToProtobuf for TokenId {
type Protobuf = services::TokenId;
fn to_protobuf(&self) -> Self::Protobuf {
services::TokenId {
token_num: self.num as i64,
realm_num: self.realm as i64,
shard_num: self.shard as i64,
}
}
}
impl From<u64> for TokenId {
fn from(num: u64) -> Self {
Self { num, shard: 0, realm: 0, checksum: None }
}
}
impl FromStr for TokenId {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
EntityId::from_str(s).map(Self::from)
}
}
impl From<EntityId> for TokenId {
fn from(value: EntityId) -> Self {
let EntityId { shard, realm, num, checksum } = value;
Self { shard, realm, num, checksum }
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use expect_test::expect;
use crate::TokenId;
#[test]
fn parse() {
expect!["0.0.5005"].assert_eq(&TokenId::from_str("0.0.5005").unwrap().to_string());
}
#[test]
fn from_bytes() {
expect!["0.0.5005"].assert_eq(
&TokenId::from_bytes(&TokenId::new(0, 0, 5005).to_bytes()).unwrap().to_string(),
);
}
#[test]
fn from_solidity_address() {
expect!["0.0.5005"].assert_eq(
&TokenId::from_solidity_address("000000000000000000000000000000000000138D")
.unwrap()
.to_string(),
);
}
#[test]
fn to_solidity_address() {
expect!["000000000000000000000000000000000000138d"]
.assert_eq(&TokenId::new(0, 0, 5005).to_solidity_address().unwrap());
}
}