use std::fmt::{self, Debug, Formatter};
use std::future::Future;
use http::HeaderMap;
use r402_protocol::{ClientError, PaymentRequired};
use crate::hooks::BoxFuture;
use crate::register::PaymentClient;
use crate::select::PaymentSelector;
pub trait ClientExtension: Send + Sync {
fn key(&self) -> &'static str;
fn enrich_payment_payload<'a>(
&'a self,
payload_b64: &'a str,
_payment_required: &'a PaymentRequired,
) -> impl Future<Output = Result<String, ClientError>> + Send + 'a {
std::future::ready(Ok(payload_b64.to_owned()))
}
fn on_payment_required<'a>(
&'a self,
_payment_required: &'a PaymentRequired,
_request_url: &'a str,
) -> impl Future<Output = HeaderMap> + Send + 'a {
std::future::ready(HeaderMap::new())
}
}
pub trait DynClientExtension: Send + Sync {
fn key(&self) -> &'static str;
fn enrich_payment_payload<'a>(
&'a self,
payload_b64: &'a str,
payment_required: &'a PaymentRequired,
) -> BoxFuture<'a, Result<String, ClientError>>;
fn on_payment_required<'a>(
&'a self,
payment_required: &'a PaymentRequired,
request_url: &'a str,
) -> BoxFuture<'a, HeaderMap>;
}
impl<T: ClientExtension + ?Sized> DynClientExtension for T {
fn key(&self) -> &'static str {
<Self as ClientExtension>::key(self)
}
fn enrich_payment_payload<'a>(
&'a self,
payload_b64: &'a str,
payment_required: &'a PaymentRequired,
) -> BoxFuture<'a, Result<String, ClientError>> {
Box::pin(<Self as ClientExtension>::enrich_payment_payload(
self,
payload_b64,
payment_required,
))
}
fn on_payment_required<'a>(
&'a self,
payment_required: &'a PaymentRequired,
request_url: &'a str,
) -> BoxFuture<'a, HeaderMap> {
Box::pin(<Self as ClientExtension>::on_payment_required(
self,
payment_required,
request_url,
))
}
}
impl Debug for dyn DynClientExtension {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("DynClientExtension")
}
}
impl<S: PaymentSelector> PaymentClient<S> {
pub(crate) async fn enrich_signed_payload(
&self,
payload_b64: &str,
payment_required: &PaymentRequired,
) -> Result<String, ClientError> {
let mut payload = payload_b64.to_owned();
for extension in &self.extensions {
payload = extension
.enrich_payment_payload(&payload, payment_required)
.await?;
}
Ok(payload)
}
pub async fn extension_headers(
&self,
payment_required: &PaymentRequired,
request_url: &str,
) -> HeaderMap {
let mut headers = HeaderMap::new();
for extension in &self.extensions {
if payment_required.extensions.get(extension.key()).is_none() {
continue;
}
headers.extend(
extension
.on_payment_required(payment_required, request_url)
.await,
);
}
headers
}
}