use std::str::FromStr;
use compact_str::CompactString;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::chain::ChainId;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[non_exhaustive]
pub struct PaymentRequirements<
TScheme = CompactString,
TAmount = CompactString,
TAddress = CompactString,
TExtra = serde_json::Value,
> {
pub scheme: TScheme,
pub network: ChainId,
pub amount: TAmount,
pub pay_to: TAddress,
pub max_timeout_seconds: u64,
pub asset: TAddress,
#[serde(default = "Option::default", skip_serializing_if = "Option::is_none")]
pub extra: Option<TExtra>,
}
impl<TScheme, TAmount, TAddress, TExtra> PaymentRequirements<TScheme, TAmount, TAddress, TExtra> {
#[must_use]
pub const fn new(
scheme: TScheme,
network: ChainId,
amount: TAmount,
pay_to: TAddress,
asset: TAddress,
max_timeout_seconds: u64,
) -> Self {
Self {
scheme,
network,
amount,
pay_to,
asset,
max_timeout_seconds,
extra: None,
}
}
#[must_use]
pub fn with_extra(mut self, extra: TExtra) -> Self {
self.extra = Some(extra);
self
}
#[must_use]
pub fn with_optional_extra(mut self, extra: Option<TExtra>) -> Self {
self.extra = extra;
self
}
}
impl PaymentRequirements {
#[must_use]
pub fn as_concrete<TScheme, TAmount, TAddress, TExtra>(
&self,
) -> Option<PaymentRequirements<TScheme, TAmount, TAddress, TExtra>>
where
TScheme: FromStr,
TAmount: FromStr,
TAddress: FromStr,
TExtra: DeserializeOwned,
{
let scheme = self.scheme.parse::<TScheme>().ok()?;
let amount = self.amount.parse::<TAmount>().ok()?;
let pay_to = self.pay_to.parse::<TAddress>().ok()?;
let asset = self.asset.parse::<TAddress>().ok()?;
let extra = self
.extra
.as_ref()
.and_then(|v| serde_json::from_value(v.clone()).ok());
Some(PaymentRequirements {
scheme,
network: self.network.clone(),
amount,
pay_to,
max_timeout_seconds: self.max_timeout_seconds,
asset,
extra,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_unknown_top_level_field() {
let json = serde_json::json!({
"scheme": "exact",
"network": "eip155:8453",
"amount": "1",
"payTo": "0x0",
"maxTimeoutSeconds": 60,
"asset": "0x0",
"unknownField": 1
});
assert!(serde_json::from_value::<PaymentRequirements>(json).is_err());
}
}