#![cfg(feature = "network")]
use async_trait::async_trait;
use crate::remittance::error::RemittanceError;
use crate::remittance::types::{Invoice, ModuleContext, Settlement, Termination};
#[derive(Debug)]
pub enum BuildSettlementResult<A> {
Settle { artifact: A },
Terminate { termination: Termination },
}
#[derive(Debug)]
pub enum AcceptSettlementResult<R> {
Accept { receipt_data: Option<R> },
Terminate { termination: Termination },
}
#[async_trait]
pub trait RemittanceModule: Send + Sync {
type OptionTerms: Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static;
type SettlementArtifact: Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static;
type ReceiptData: Send + Sync + serde::Serialize + serde::de::DeserializeOwned + 'static;
fn id(&self) -> &str;
fn name(&self) -> &str;
fn allow_unsolicited_settlements(&self) -> bool;
fn supports_create_option(&self) -> bool {
false
}
async fn create_option(
&self,
thread_id: &str,
invoice: &Invoice,
ctx: &ModuleContext,
) -> Result<Self::OptionTerms, RemittanceError> {
let _ = (thread_id, invoice, ctx);
Err(RemittanceError::Protocol(
"createOption not implemented for this module".into(),
))
}
async fn build_settlement(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
option: &Self::OptionTerms,
note: Option<&str>,
ctx: &ModuleContext,
) -> Result<BuildSettlementResult<Self::SettlementArtifact>, RemittanceError>;
async fn accept_settlement(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
settlement: &Self::SettlementArtifact,
sender: &str,
ctx: &ModuleContext,
) -> Result<AcceptSettlementResult<Self::ReceiptData>, RemittanceError>;
async fn process_receipt(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
receipt_data: &Self::ReceiptData,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError> {
let _ = (thread_id, invoice, receipt_data, sender, ctx);
Ok(())
}
async fn process_termination(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
settlement: Option<&Settlement>,
termination: &Termination,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError> {
let _ = (thread_id, invoice, settlement, termination, sender, ctx);
Ok(())
}
}
pub struct BuildSettlementErased {
pub action: &'static str,
pub artifact: Option<serde_json::Value>,
pub termination: Option<Termination>,
}
pub struct AcceptSettlementErased {
pub action: &'static str,
pub receipt_data: Option<serde_json::Value>,
pub termination: Option<Termination>,
}
#[async_trait]
pub trait ErasedRemittanceModule: Send + Sync {
fn id(&self) -> &str;
fn name(&self) -> &str;
fn allow_unsolicited_settlements(&self) -> bool;
fn supports_create_option(&self) -> bool;
async fn create_option_erased(
&self,
thread_id: &str,
invoice: &Invoice,
ctx: &ModuleContext,
) -> Result<serde_json::Value, RemittanceError>;
async fn build_settlement_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
option_json: &serde_json::Value,
note: Option<&str>,
ctx: &ModuleContext,
) -> Result<BuildSettlementErased, RemittanceError>;
async fn accept_settlement_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
artifact_json: &serde_json::Value,
sender: &str,
ctx: &ModuleContext,
) -> Result<AcceptSettlementErased, RemittanceError>;
async fn process_receipt_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
receipt_json: &serde_json::Value,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError>;
async fn process_termination_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
settlement: Option<&Settlement>,
termination: &Termination,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError>;
}
#[async_trait]
impl<T: RemittanceModule> ErasedRemittanceModule for T {
fn id(&self) -> &str {
RemittanceModule::id(self)
}
fn name(&self) -> &str {
RemittanceModule::name(self)
}
fn allow_unsolicited_settlements(&self) -> bool {
RemittanceModule::allow_unsolicited_settlements(self)
}
fn supports_create_option(&self) -> bool {
RemittanceModule::supports_create_option(self)
}
async fn create_option_erased(
&self,
thread_id: &str,
invoice: &Invoice,
ctx: &ModuleContext,
) -> Result<serde_json::Value, RemittanceError> {
let typed = self.create_option(thread_id, invoice, ctx).await?;
let v = serde_json::to_value(typed)?;
Ok(v)
}
async fn build_settlement_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
option_json: &serde_json::Value,
note: Option<&str>,
ctx: &ModuleContext,
) -> Result<BuildSettlementErased, RemittanceError> {
let option: T::OptionTerms = serde_json::from_value(option_json.clone())?;
let result = self
.build_settlement(thread_id, invoice, &option, note, ctx)
.await?;
match result {
BuildSettlementResult::Settle { artifact } => {
let v = serde_json::to_value(artifact)?;
Ok(BuildSettlementErased {
action: "settle",
artifact: Some(v),
termination: None,
})
}
BuildSettlementResult::Terminate { termination } => Ok(BuildSettlementErased {
action: "terminate",
artifact: None,
termination: Some(termination),
}),
}
}
async fn accept_settlement_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
artifact_json: &serde_json::Value,
sender: &str,
ctx: &ModuleContext,
) -> Result<AcceptSettlementErased, RemittanceError> {
let artifact: T::SettlementArtifact = serde_json::from_value(artifact_json.clone())?;
let result = self
.accept_settlement(thread_id, invoice, &artifact, sender, ctx)
.await?;
match result {
AcceptSettlementResult::Accept { receipt_data } => {
let receipt_json = match receipt_data {
Some(rd) => Some(serde_json::to_value(rd)?),
None => None,
};
Ok(AcceptSettlementErased {
action: "accept",
receipt_data: receipt_json,
termination: None,
})
}
AcceptSettlementResult::Terminate { termination } => Ok(AcceptSettlementErased {
action: "terminate",
receipt_data: None,
termination: Some(termination),
}),
}
}
async fn process_receipt_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
receipt_json: &serde_json::Value,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError> {
let receipt_data: T::ReceiptData = serde_json::from_value(receipt_json.clone())?;
self.process_receipt(thread_id, invoice, &receipt_data, sender, ctx)
.await
}
async fn process_termination_erased(
&self,
thread_id: &str,
invoice: Option<&Invoice>,
settlement: Option<&Settlement>,
termination: &Termination,
sender: &str,
ctx: &ModuleContext,
) -> Result<(), RemittanceError> {
self.process_termination(thread_id, invoice, settlement, termination, sender, ctx)
.await
}
}