use crate::models::transactions::mptoken_issuance_set::validate_mptoken_issuance_id;
use crate::models::{Model, XRPLModelResult};
use alloc::{borrow::Cow, string::ToString};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct MPTAmount<'a> {
pub value: Cow<'a, str>,
pub mpt_issuance_id: Cow<'a, str>,
}
impl<'a> Model for MPTAmount<'a> {
fn get_errors(&self) -> XRPLModelResult<()> {
if self.value.is_empty() || !self.value.bytes().all(|b| b.is_ascii_digit()) {
return Err(crate::models::XRPLModelException::InvalidValueFormat {
field: "value".into(),
format: "unsigned integer string".into(),
found: self.value.to_string(),
});
}
let n: u64 = self.value.parse()?;
if n > i64::MAX as u64 {
return Err(crate::models::XRPLModelException::InvalidValue {
field: "value".into(),
expected: alloc::format!("MPT amount <= {} (i64::MAX)", i64::MAX),
found: self.value.to_string(),
});
}
validate_mptoken_issuance_id(self.mpt_issuance_id.as_ref())?;
Ok(())
}
}
impl<'a> MPTAmount<'a> {
pub fn new(value: Cow<'a, str>, mpt_issuance_id: Cow<'a, str>) -> Self {
Self {
value,
mpt_issuance_id,
}
}
}
#[cfg(test)]
mod tests {
use crate::models::Model;
use super::*;
const VALID_ID: &str = "00000001A407AF5856CEFBF81F3D4A0000000000A407AF58";
#[test]
fn test_mpt_amount_serde_roundtrip() {
let amount = MPTAmount::new("100".into(), VALID_ID.into());
let json = serde_json::to_string(&amount).unwrap();
let decoded: MPTAmount = serde_json::from_str(&json).unwrap();
assert_eq!(amount, decoded);
}
#[test]
fn test_mpt_amount_get_errors_valid() {
let amount = MPTAmount::new("9223372036854775807".into(), VALID_ID.into());
assert!(amount.get_errors().is_ok());
}
#[test]
fn test_mpt_amount_get_errors_zero() {
let amount = MPTAmount::new("0".into(), VALID_ID.into());
assert!(amount.get_errors().is_ok());
}
#[test]
fn test_mpt_amount_get_errors_bad_value_decimal() {
let amount = MPTAmount::new("1.5".into(), VALID_ID.into());
assert!(amount.get_errors().is_err());
}
#[test]
fn test_mpt_amount_get_errors_bad_value_negative() {
let amount = MPTAmount::new("-1".into(), VALID_ID.into());
assert!(amount.get_errors().is_err());
}
#[test]
fn test_mpt_amount_get_errors_bad_value_plus_prefix() {
let amount = MPTAmount::new("+1".into(), VALID_ID.into());
assert!(amount.get_errors().is_err());
}
#[test]
fn test_mpt_amount_get_errors_rejects_above_i64_max() {
let amount = MPTAmount::new("9223372036854775808".into(), VALID_ID.into());
assert!(amount.get_errors().is_err());
}
#[test]
fn test_mpt_amount_get_errors_bad_id_too_short() {
let amount = MPTAmount::new("100".into(), "DEAD".into());
assert!(amount.get_errors().is_err());
}
#[test]
fn test_mpt_amount_get_errors_bad_id_non_hex() {
let bad_id = "Z".repeat(48);
let amount = MPTAmount::new("100".into(), bad_id.as_str().into());
assert!(amount.get_errors().is_err());
}
}