use serde::{Deserialize, Serialize};
use crate::error::{MppError, Result};
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SessionRequest {
pub amount: String,
#[serde(rename = "unitType", skip_serializing_if = "Option::is_none")]
pub unit_type: Option<String>,
pub currency: String,
#[serde(skip)]
pub decimals: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recipient: Option<String>,
#[serde(rename = "suggestedDeposit", skip_serializing_if = "Option::is_none")]
pub suggested_deposit: Option<String>,
#[serde(rename = "methodDetails", skip_serializing_if = "Option::is_none")]
pub method_details: Option<serde_json::Value>,
}
impl SessionRequest {
pub fn with_base_units(mut self) -> Result<Self> {
if let Some(decimals) = self.decimals {
self.amount = super::parse_units(&self.amount, decimals)?;
if let Some(ref deposit) = self.suggested_deposit {
self.suggested_deposit = Some(super::parse_units(deposit, decimals)?);
}
self.decimals = None;
}
Ok(self)
}
pub fn parse_amount(&self) -> Result<u128> {
self.amount
.parse()
.map_err(|_| MppError::InvalidAmount(format!("Invalid amount: {}", self.amount)))
}
pub fn validate_max_amount(&self, max_amount: &str) -> Result<()> {
let amount = self.parse_amount()?;
let max: u128 = max_amount
.parse()
.map_err(|_| MppError::InvalidAmount(format!("Invalid max amount: {}", max_amount)))?;
if amount > max {
return Err(MppError::AmountExceedsMax {
required: amount,
max,
});
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_session_request_serialization() {
let req = SessionRequest {
amount: "1000".to_string(),
unit_type: Some("second".to_string()),
currency: "0x123".to_string(),
recipient: Some("0x456".to_string()),
suggested_deposit: Some("60000".to_string()),
method_details: Some(serde_json::json!({
"chainId": 42431,
"feePayer": true
})),
..Default::default()
};
let json = serde_json::to_string(&req).unwrap();
assert!(json.contains("\"amount\":\"1000\""));
assert!(json.contains("\"unitType\":\"second\""));
assert!(json.contains("\"suggestedDeposit\":\"60000\""));
assert!(json.contains("\"methodDetails\""));
assert!(json.contains("\"chainId\":42431"));
let parsed: SessionRequest = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.amount, "1000");
assert_eq!(parsed.unit_type, Some("second".to_string()));
assert_eq!(parsed.suggested_deposit.as_deref(), Some("60000"));
}
#[test]
fn test_session_request_optional_fields_omitted() {
let req = SessionRequest {
amount: "500".to_string(),
unit_type: Some("request".to_string()),
currency: "USD".to_string(),
..Default::default()
};
let json = serde_json::to_string(&req).unwrap();
assert!(!json.contains("recipient"));
assert!(!json.contains("suggestedDeposit"));
assert!(!json.contains("methodDetails"));
}
#[test]
fn test_session_request_deserialization() {
let json = r#"{"amount":"2000","unitType":"minute","currency":"0xabc"}"#;
let parsed: SessionRequest = serde_json::from_str(json).unwrap();
assert_eq!(parsed.amount, "2000");
assert_eq!(parsed.unit_type, Some("minute".to_string()));
assert_eq!(parsed.currency, "0xabc");
assert!(parsed.recipient.is_none());
assert!(parsed.suggested_deposit.is_none());
assert!(parsed.method_details.is_none());
}
#[test]
fn test_parse_amount() {
let req = SessionRequest {
amount: "1000000".to_string(),
..Default::default()
};
assert_eq!(req.parse_amount().unwrap(), 1_000_000u128);
let invalid = SessionRequest {
amount: "not-a-number".to_string(),
..Default::default()
};
assert!(invalid.parse_amount().is_err());
}
#[test]
fn test_validate_max_amount() {
let req = SessionRequest {
amount: "1000".to_string(),
..Default::default()
};
assert!(req.validate_max_amount("2000").is_ok());
assert!(req.validate_max_amount("1000").is_ok());
assert!(req.validate_max_amount("500").is_err());
}
#[test]
fn test_with_base_units() {
let req = SessionRequest {
amount: "1.5".to_string(),
unit_type: Some("second".to_string()),
currency: "0x123".to_string(),
decimals: Some(6),
suggested_deposit: Some("60".to_string()),
..Default::default()
};
let converted = req.with_base_units().unwrap();
assert_eq!(converted.amount, "1500000");
assert_eq!(converted.suggested_deposit.as_deref(), Some("60000000"));
assert!(converted.decimals.is_none());
}
#[test]
fn test_with_base_units_no_decimals() {
let req = SessionRequest {
amount: "1000000".to_string(),
unit_type: Some("second".to_string()),
currency: "0x123".to_string(),
..Default::default()
};
let converted = req.with_base_units().unwrap();
assert_eq!(converted.amount, "1000000");
}
#[test]
fn test_session_request_without_unit_type() {
let json = r#"{"amount":"2000","currency":"0xabc"}"#;
let parsed: SessionRequest = serde_json::from_str(json).unwrap();
assert_eq!(parsed.amount, "2000");
assert!(parsed.unit_type.is_none());
}
}