use std::future::Future;
use std::sync::Arc;
use affinidi_messaging_didcomm::DIDCommAgent;
use chrono::{DateTime, Utc};
use serde::de::DeserializeOwned;
use serde::Serialize;
use trust_tasks_rs::{
consume_inbound, ConsumeChecks, ConsumeOutcome, ErrorResponse, FreshnessPolicy,
InMemoryReplayGuard, Payload, PayloadPolicy, PayloadValidator, ProofPolicy, ProofVerifier,
ReplayGuard, ReplayPolicy, ResolvedParties, TrustTask,
};
use crate::error::DidcommError;
use crate::handler::DidcommHandler;
use crate::pack::{unpack_trust_task, unpack_trust_task_from, SenderAllowlist};
enum Record {
InProcess(InMemoryReplayGuard),
Shared(Arc<dyn ReplayGuard>),
Disabled,
}
pub struct DidcommConsumer {
record: Record,
freshness: FreshnessPolicy,
}
impl std::fmt::Debug for DidcommConsumer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let record = match &self.record {
Record::InProcess(guard) => format!("in-process ({} records)", guard.len()),
Record::Shared(_) => "caller-supplied".to_string(),
Record::Disabled => "disabled".to_string(),
};
f.debug_struct("DidcommConsumer")
.field("replay_record", &record)
.field("freshness", &self.freshness)
.finish()
}
}
impl Default for DidcommConsumer {
fn default() -> Self {
Self::new()
}
}
impl DidcommConsumer {
pub fn new() -> Self {
Self {
record: Record::InProcess(InMemoryReplayGuard::default()),
freshness: FreshnessPolicy::consequential(),
}
}
pub fn with_replay_guard(guard: Arc<dyn ReplayGuard>) -> Self {
Self {
record: Record::Shared(guard),
freshness: FreshnessPolicy::consequential(),
}
}
#[must_use]
pub fn without_replay_record() -> Self {
Self {
record: Record::Disabled,
freshness: FreshnessPolicy::default(),
}
}
#[must_use]
pub fn with_freshness(mut self, freshness: FreshnessPolicy) -> Self {
self.freshness = freshness;
self
}
pub fn freshness(&self) -> &FreshnessPolicy {
&self.freshness
}
pub fn replay_guard(&self) -> Option<&dyn ReplayGuard> {
match &self.record {
Record::InProcess(guard) => Some(guard),
Record::Shared(guard) => Some(guard.as_ref()),
Record::Disabled => None,
}
}
pub fn checks(&self) -> ConsumeChecks<'_> {
ConsumeChecks {
freshness: self.freshness,
replay: match self.replay_guard() {
Some(guard) => ReplayPolicy::Guard(guard),
None => ReplayPolicy::NotConsequential,
},
}
}
#[allow(clippy::too_many_arguments)]
pub async fn receive<P, R, V, W, F, Fut>(
&self,
wire: &str,
agent: &DIDCommAgent,
expected_sender_did: Option<&str>,
my_vid: &str,
proof_policy: ProofPolicy<'_, V>,
payload_policy: PayloadPolicy<'_, W>,
now: DateTime<Utc>,
error_id_factory: impl FnOnce() -> String,
handler: F,
) -> Result<ConsumeOutcome<R>, DidcommError>
where
P: Payload + Serialize + DeserializeOwned + Send + Sync,
R: Serialize,
V: ProofVerifier + ?Sized,
W: PayloadValidator + ?Sized,
F: FnOnce(TrustTask<P>, ResolvedParties) -> Fut,
Fut: Future<Output = Result<TrustTask<R>, ErrorResponse>>,
{
let (doc, transport) = unpack_trust_task::<P>(wire, agent, expected_sender_did)?;
Ok(self
.consume(
&transport,
doc,
my_vid,
proof_policy,
payload_policy,
now,
error_id_factory,
handler,
)
.await)
}
#[allow(clippy::too_many_arguments)]
pub async fn receive_from<P, R, V, W, F, Fut>(
&self,
wire: &str,
agent: &DIDCommAgent,
allowlist: &SenderAllowlist,
my_vid: &str,
proof_policy: ProofPolicy<'_, V>,
payload_policy: PayloadPolicy<'_, W>,
now: DateTime<Utc>,
error_id_factory: impl FnOnce() -> String,
handler: F,
) -> Result<ConsumeOutcome<R>, DidcommError>
where
P: Payload + Serialize + DeserializeOwned + Send + Sync,
R: Serialize,
V: ProofVerifier + ?Sized,
W: PayloadValidator + ?Sized,
F: FnOnce(TrustTask<P>, ResolvedParties) -> Fut,
Fut: Future<Output = Result<TrustTask<R>, ErrorResponse>>,
{
let (doc, transport) = unpack_trust_task_from::<P>(wire, agent, allowlist)?;
Ok(self
.consume(
&transport,
doc,
my_vid,
proof_policy,
payload_policy,
now,
error_id_factory,
handler,
)
.await)
}
#[allow(clippy::too_many_arguments)]
pub async fn consume<P, R, V, W, F, Fut>(
&self,
transport: &DidcommHandler,
doc: TrustTask<P>,
my_vid: &str,
proof_policy: ProofPolicy<'_, V>,
payload_policy: PayloadPolicy<'_, W>,
now: DateTime<Utc>,
error_id_factory: impl FnOnce() -> String,
handler: F,
) -> ConsumeOutcome<R>
where
P: Payload + Serialize + Send + Sync,
R: Serialize,
V: ProofVerifier + ?Sized,
W: PayloadValidator + ?Sized,
F: FnOnce(TrustTask<P>, ResolvedParties) -> Fut,
Fut: Future<Output = Result<TrustTask<R>, ErrorResponse>>,
{
consume_inbound(
transport,
proof_policy,
payload_policy,
self.checks(),
doc,
my_vid,
now,
error_id_factory,
handler,
)
.await
}
}