use serde::Serialize;
use crate::document::TrustTask;
use crate::transport::{TransportContext, TransportHandler};
#[derive(Debug, Clone, Default)]
pub struct InMemoryHandler {
pub local: Option<String>,
pub peer: Option<String>,
}
impl InMemoryHandler {
pub fn new() -> Self {
Self::default()
}
pub fn with_local(mut self, vid: impl Into<String>) -> Self {
self.local = Some(vid.into());
self
}
pub fn with_peer(mut self, vid: impl Into<String>) -> Self {
self.peer = Some(vid.into());
self
}
}
impl TransportHandler for InMemoryHandler {
fn binding_uri(&self) -> &str {
"urn:trust-tasks:binding:in-memory"
}
fn derive_parties(&self) -> TransportContext {
TransportContext {
issuer: self.peer.clone(),
recipient: self.local.clone(),
}
}
fn prepare_outbound<P: Serialize>(&self, doc: &mut TrustTask<P>) {
if let (Some(local), Some(in_band)) = (self.local.as_deref(), doc.issuer.as_deref()) {
if local == in_band {
doc.issuer = None;
}
}
if let (Some(peer), Some(in_band)) = (self.peer.as_deref(), doc.recipient.as_deref()) {
if peer == in_band {
doc.recipient = None;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::type_uri::TypeUri;
fn doc(issuer: Option<&str>, recipient: Option<&str>) -> TrustTask<serde_json::Value> {
let mut d = TrustTask::new(
"id-1",
TypeUri::canonical("kyc-handoff", 1, 0).unwrap(),
serde_json::json!({}),
);
d.issuer = issuer.map(str::to_string);
d.recipient = recipient.map(str::to_string);
d
}
#[test]
fn fills_in_absent_parties_from_transport() {
let h = InMemoryHandler::new()
.with_local("did:web:bank.example")
.with_peer("did:web:verifier.example");
let resolved = h.resolve_parties(&doc(None, None)).unwrap();
assert_eq!(resolved.issuer.as_deref(), Some("did:web:verifier.example"));
assert_eq!(resolved.recipient.as_deref(), Some("did:web:bank.example"));
}
#[test]
fn in_band_wins_when_consistent() {
let h = InMemoryHandler::new()
.with_local("did:web:bank.example")
.with_peer("did:web:verifier.example");
let resolved = h
.resolve_parties(&doc(
Some("did:web:verifier.example"),
Some("did:web:bank.example"),
))
.unwrap();
assert_eq!(resolved.issuer.as_deref(), Some("did:web:verifier.example"));
assert_eq!(resolved.recipient.as_deref(), Some("did:web:bank.example"));
}
#[test]
fn flags_issuer_mismatch() {
let h = InMemoryHandler::new()
.with_local("did:web:bank.example")
.with_peer("did:web:verifier.example");
let err = h
.resolve_parties(&doc(Some("did:web:attacker.example"), None))
.unwrap_err();
assert!(matches!(
err,
crate::ConsistencyError::IssuerMismatch { .. }
));
}
#[test]
fn prepare_outbound_strips_redundant_in_band() {
let h = InMemoryHandler::new()
.with_local("did:web:bank.example")
.with_peer("did:web:verifier.example");
let mut d = doc(
Some("did:web:bank.example"),
Some("did:web:verifier.example"),
);
h.prepare_outbound(&mut d);
assert!(d.issuer.is_none());
assert!(d.recipient.is_none());
}
#[test]
fn prepare_outbound_keeps_in_band_when_disagreeing() {
let h = InMemoryHandler::new()
.with_local("did:web:bank.example")
.with_peer("did:web:verifier.example");
let mut d = doc(Some("did:web:forwarded.example"), None);
h.prepare_outbound(&mut d);
assert_eq!(d.issuer.as_deref(), Some("did:web:forwarded.example"));
}
}