use radix_common::math::ParseDecimalError;
use radix_common::prelude::*;
#[derive(Debug)]
pub enum ParseResourceSpecifierError {
InvalidAmount(ParseDecimalError),
InvalidResourceAddress(String),
InvalidNonFungibleLocalId(ParseNonFungibleLocalIdError),
MoreThanOneAmountSpecified,
}
#[derive(Debug, PartialEq, Eq)]
pub enum ResourceSpecifier {
Amount(Decimal, ResourceAddress),
Ids(IndexSet<NonFungibleLocalId>, ResourceAddress),
}
impl FromStr for ResourceSpecifier {
type Err = ParseResourceSpecifierError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_resource_specifier(s, &AddressBech32Decoder::for_simulator())
}
}
pub fn parse_resource_specifier(
input: &str,
address_bech32_decoder: &AddressBech32Decoder,
) -> Result<ResourceSpecifier, ParseResourceSpecifierError> {
let tokens = input
.trim()
.split(':')
.map(|s| s.trim())
.collect::<Vec<_>>();
if tokens.len() != 2 {
return Err(ParseResourceSpecifierError::MoreThanOneAmountSpecified);
}
let is_non_fungible = tokens[1].starts_with("<")
|| tokens[1].starts_with("#")
|| tokens[1].starts_with("[")
|| tokens[1].starts_with("{");
if !is_non_fungible {
let resource_address_string = tokens[0];
let amount_string = tokens[1];
let amount = amount_string
.parse()
.map_err(ParseResourceSpecifierError::InvalidAmount)?;
let resource_address =
ResourceAddress::try_from_bech32(address_bech32_decoder, resource_address_string)
.ok_or(ParseResourceSpecifierError::InvalidResourceAddress(
resource_address_string.to_string(),
))?;
Ok(ResourceSpecifier::Amount(amount, resource_address))
} else {
let resource_address_string = tokens[0];
let resource_address =
ResourceAddress::try_from_bech32(address_bech32_decoder, resource_address_string)
.ok_or(ParseResourceSpecifierError::InvalidResourceAddress(
resource_address_string.to_string(),
))?;
let non_fungible_local_ids = tokens[1]
.split(',')
.map(|s| NonFungibleLocalId::from_str(s.trim()))
.collect::<Result<IndexSet<_>, _>>()
.map_err(ParseResourceSpecifierError::InvalidNonFungibleLocalId)?;
Ok(ResourceSpecifier::Ids(
non_fungible_local_ids,
resource_address,
))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn parsing_of_fungible_resource_specifier_succeeds() {
let resource_specifier_string =
"resource_sim1thvwu8dh6lk4y9mntemkvj25wllq8adq42skzufp4m8wxxuemugnez:900";
let address_bech32_decoder = AddressBech32Decoder::for_simulator();
let resource_specifier =
parse_resource_specifier(resource_specifier_string, &address_bech32_decoder)
.expect("Failed to parse resource specifier");
assert_eq!(
resource_specifier,
ResourceSpecifier::Amount(
900.into(),
ResourceAddress::try_from_bech32(
&address_bech32_decoder,
"resource_sim1thvwu8dh6lk4y9mntemkvj25wllq8adq42skzufp4m8wxxuemugnez"
)
.unwrap()
)
)
}
#[test]
pub fn parsing_of_single_non_fungible_resource_specifier_succeeds() {
let resource_specifier_string =
"resource_sim1nfxxxxxxxxxxsecpsgxxxxxxxxx004638826440xxxxxxxxxwj8qq5:[1f5db2614c1c4c626e9c279349b240af7cb939ead29058fdff2c]";
let address_bech32_decoder = AddressBech32Decoder::for_simulator();
let resource_specifier =
parse_resource_specifier(resource_specifier_string, &address_bech32_decoder)
.expect("Failed to parse resource specifier");
assert_eq!(
resource_specifier,
ResourceSpecifier::Ids(
IndexSet::from([NonFungibleLocalId::bytes(vec![
31, 93, 178, 97, 76, 28, 76, 98, 110, 156, 39, 147, 73, 178, 64, 175, 124, 185,
57, 234, 210, 144, 88, 253, 255, 44
])
.unwrap()]),
SECP256K1_SIGNATURE_RESOURCE
)
)
}
#[test]
pub fn parsing_of_multiple_non_fungible_resource_specifier_succeeds() {
let resource_specifier_string =
"resource_sim1nfxxxxxxxxxxsecpsgxxxxxxxxx004638826440xxxxxxxxxwj8qq5:[1f5db2614c1c4c626e9c279349b240af7cb939ead29058fdff2c],[d85dc446d8e5eff48db25b56f6b5001d14627b5a199598485a8d],[005d1ae87b0e7c5401d38e58d43291ffbd9ba6e1da54f87504a7]";
let address_bech32_decoder = AddressBech32Decoder::for_simulator();
let resource_specifier =
parse_resource_specifier(resource_specifier_string, &address_bech32_decoder)
.expect("Failed to parse resource specifier");
assert_eq!(
resource_specifier,
ResourceSpecifier::Ids(
IndexSet::from([
NonFungibleLocalId::bytes(vec![
31, 93, 178, 97, 76, 28, 76, 98, 110, 156, 39, 147, 73, 178, 64, 175, 124,
185, 57, 234, 210, 144, 88, 253, 255, 44
])
.unwrap(),
NonFungibleLocalId::bytes(vec![
216, 93, 196, 70, 216, 229, 239, 244, 141, 178, 91, 86, 246, 181, 0, 29,
20, 98, 123, 90, 25, 149, 152, 72, 90, 141
])
.unwrap(),
NonFungibleLocalId::bytes(vec![
0, 93, 26, 232, 123, 14, 124, 84, 1, 211, 142, 88, 212, 50, 145, 255, 189,
155, 166, 225, 218, 84, 248, 117, 4, 167
])
.unwrap()
]),
SECP256K1_SIGNATURE_RESOURCE
)
)
}
}