use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub trait MessageContext {
fn participant_dids(&self) -> Vec<String>;
fn routing_hints(&self) -> RoutingHints {
RoutingHints::default()
}
fn transaction_context(&self) -> Option<TransactionContext> {
None
}
fn transaction_id(&self) -> Option<String> {
self.transaction_context().map(|ctx| ctx.transaction_id)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RoutingHints {
pub preferred_endpoints: Vec<String>,
pub priority: Priority,
pub require_encryption: bool,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub enum Priority {
High,
#[default]
Normal,
Low,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransactionContext {
pub transaction_id: String,
pub parent_transaction_id: Option<String>,
pub transaction_type: String,
pub metadata: HashMap<String, serde_json::Value>,
}
impl TransactionContext {
pub fn new(transaction_id: String, transaction_type: String) -> Self {
Self {
transaction_id,
parent_transaction_id: None,
transaction_type,
metadata: HashMap::new(),
}
}
pub fn with_parent(mut self, parent_id: String) -> Self {
self.parent_transaction_id = Some(parent_id);
self
}
pub fn with_metadata(mut self, key: String, value: serde_json::Value) -> Self {
self.metadata.insert(key, value);
self
}
}
pub trait ParticipantExtractor {
fn extract_single_participant_dids(&self) -> Vec<String>;
fn extract_list_participant_dids(&self) -> Vec<String>;
fn extract_optional_participant_dids(&self) -> Vec<String>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::message::agent::TapParticipant;
use crate::message::{Agent, Party};
#[test]
fn test_routing_hints_default() {
let hints = RoutingHints::default();
assert!(hints.preferred_endpoints.is_empty());
assert!(matches!(hints.priority, Priority::Normal));
assert!(!hints.require_encryption);
assert!(hints.metadata.is_empty());
}
#[test]
fn test_transaction_context() {
let ctx = TransactionContext::new("tx-123".to_string(), "transfer".to_string())
.with_parent("parent-tx-456".to_string())
.with_metadata(
"key".to_string(),
serde_json::Value::String("value".to_string()),
);
assert_eq!(ctx.transaction_id, "tx-123");
assert_eq!(ctx.transaction_type, "transfer");
assert_eq!(ctx.parent_transaction_id, Some("parent-tx-456".to_string()));
assert_eq!(
ctx.metadata.get("key").unwrap(),
&serde_json::Value::String("value".to_string())
);
}
struct TestMessage {
originator: Party,
beneficiary: Option<Party>,
agents: Vec<Agent>,
transaction_id: String,
}
impl MessageContext for TestMessage {
fn participant_dids(&self) -> Vec<String> {
let mut dids = vec![self.originator.id().to_string()];
if let Some(ref beneficiary) = self.beneficiary {
dids.push(beneficiary.id().to_string());
}
for agent in &self.agents {
dids.push(agent.id().to_string());
}
dids
}
fn transaction_context(&self) -> Option<TransactionContext> {
Some(TransactionContext::new(
self.transaction_id.clone(),
"test".to_string(),
))
}
}
#[test]
fn test_message_context() {
let msg = TestMessage {
originator: Party::new("did:example:alice"),
beneficiary: Some(Party::new("did:example:bob")),
agents: vec![Agent::new(
"did:example:agent",
"Exchange",
"did:example:alice",
)],
transaction_id: "tx-123".to_string(),
};
let dids = msg.participant_dids();
assert_eq!(dids.len(), 3);
assert!(dids.contains(&"did:example:alice".to_string()));
assert!(dids.contains(&"did:example:bob".to_string()));
assert!(dids.contains(&"did:example:agent".to_string()));
assert_eq!(msg.transaction_id(), Some("tx-123".to_string()));
}
}