use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::message::policy::Policy;
pub trait TapParticipant {
fn id(&self) -> &str;
}
#[derive(Debug, Clone, PartialEq)]
pub struct ForParties(pub Vec<String>);
impl Serialize for ForParties {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if self.0.len() == 1 {
self.0[0].serialize(serializer)
} else {
self.0.serialize(serializer)
}
}
}
impl<'de> Deserialize<'de> for ForParties {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::{self, Visitor};
use std::fmt;
struct ForPartiesVisitor;
impl<'de> Visitor<'de> for ForPartiesVisitor {
type Value = ForParties;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string or an array of strings")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(ForParties(vec![value.to_string()]))
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let mut parties = Vec::new();
while let Some(party) = seq.next_element::<String>()? {
parties.push(party);
}
Ok(ForParties(parties))
}
}
deserializer.deserialize_any(ForPartiesVisitor)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Agent {
#[serde(rename = "@id")]
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub role: Option<String>,
#[serde(rename = "for")]
pub for_parties: ForParties,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub policies: Option<Vec<Policy>>,
#[serde(flatten)]
pub metadata: HashMap<String, serde_json::Value>,
}
impl TapParticipant for Agent {
fn id(&self) -> &str {
&self.id
}
}
impl Agent {
pub fn new(id: &str, role: &str, for_party: &str) -> Self {
Self {
id: id.to_string(),
role: Some(role.to_string()),
for_parties: ForParties(vec![for_party.to_string()]),
policies: None,
metadata: HashMap::new(),
}
}
pub fn new_for_parties(id: &str, role: &str, for_parties: Vec<String>) -> Self {
Self {
id: id.to_string(),
role: Some(role.to_string()),
for_parties: ForParties(for_parties),
policies: None,
metadata: HashMap::new(),
}
}
pub fn new_without_role(id: &str, for_party: &str) -> Self {
Self {
id: id.to_string(),
role: None,
for_parties: ForParties(vec![for_party.to_string()]),
policies: None,
metadata: HashMap::new(),
}
}
pub fn with_metadata(
id: &str,
role: &str,
for_party: &str,
metadata: HashMap<String, serde_json::Value>,
) -> Self {
Self {
id: id.to_string(),
role: Some(role.to_string()),
for_parties: ForParties(vec![for_party.to_string()]),
policies: None,
metadata,
}
}
pub fn with_policies(mut self, policies: Vec<Policy>) -> Self {
self.policies = Some(policies);
self
}
pub fn add_policy(mut self, policy: Policy) -> Self {
match &mut self.policies {
Some(policies) => policies.push(policy),
None => self.policies = Some(vec![policy]),
}
self
}
pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
self.metadata.insert(key, value);
}
pub fn with_metadata_field(mut self, key: String, value: serde_json::Value) -> Self {
self.metadata.insert(key, value);
self
}
pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
self.metadata.get(key)
}
pub fn has_role(&self, role: &str) -> bool {
self.role.as_ref().is_some_and(|r| r == role)
}
pub fn acts_for(&self, party_id: &str) -> bool {
self.for_parties.0.contains(&party_id.to_string())
}
pub fn for_parties(&self) -> &[String] {
&self.for_parties.0
}
pub fn primary_party(&self) -> Option<&str> {
self.for_parties.0.first().map(|s| s.as_str())
}
pub fn add_for_party(&mut self, party_id: &str) {
if !self.for_parties.0.contains(&party_id.to_string()) {
self.for_parties.0.push(party_id.to_string());
}
}
pub fn set_for_parties(&mut self, parties: Vec<String>) {
self.for_parties.0 = parties;
}
pub fn with_name(mut self, name: &str) -> Self {
self.metadata.insert(
"name".to_string(),
serde_json::Value::String(name.to_string()),
);
self
}
pub fn name(&self) -> Option<&str> {
self.metadata.get("name").and_then(|v| v.as_str())
}
pub fn with_url(mut self, url: &str) -> Self {
self.metadata.insert(
"url".to_string(),
serde_json::Value::String(url.to_string()),
);
self
}
pub fn url(&self) -> Option<&str> {
self.metadata.get("url").and_then(|v| v.as_str())
}
pub fn with_logo(mut self, logo: &str) -> Self {
self.metadata.insert(
"logo".to_string(),
serde_json::Value::String(logo.to_string()),
);
self
}
pub fn logo(&self) -> Option<&str> {
self.metadata.get("logo").and_then(|v| v.as_str())
}
pub fn with_description(mut self, description: &str) -> Self {
self.metadata.insert(
"description".to_string(),
serde_json::Value::String(description.to_string()),
);
self
}
pub fn description(&self) -> Option<&str> {
self.metadata.get("description").and_then(|v| v.as_str())
}
pub fn with_email(mut self, email: &str) -> Self {
self.metadata.insert(
"email".to_string(),
serde_json::Value::String(email.to_string()),
);
self
}
pub fn email(&self) -> Option<&str> {
self.metadata.get("email").and_then(|v| v.as_str())
}
pub fn with_telephone(mut self, telephone: &str) -> Self {
self.metadata.insert(
"telephone".to_string(),
serde_json::Value::String(telephone.to_string()),
);
self
}
pub fn telephone(&self) -> Option<&str> {
self.metadata.get("telephone").and_then(|v| v.as_str())
}
pub fn with_service_url(mut self, service_url: &str) -> Self {
self.metadata.insert(
"serviceUrl".to_string(),
serde_json::Value::String(service_url.to_string()),
);
self
}
pub fn service_url(&self) -> Option<&str> {
self.metadata.get("serviceUrl").and_then(|v| v.as_str())
}
}
pub mod roles {
pub const SETTLEMENT_ADDRESS: &str = "SettlementAddress";
pub const SOURCE_ADDRESS: &str = "SourceAddress";
pub const CUSTODIAL_SERVICE: &str = "CustodialService";
pub const WALLET_SERVICE: &str = "WalletService";
pub const EXCHANGE: &str = "Exchange";
pub const BRIDGE: &str = "Bridge";
pub const DEFI_PROTOCOL: &str = "DeFiProtocol";
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_agent_creation() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice");
assert_eq!(agent.id, "did:web:example.com");
assert_eq!(agent.role, Some("Exchange".to_string()));
assert_eq!(agent.for_parties.0, vec!["did:example:alice"]);
assert!(agent.policies.is_none());
assert!(agent.metadata.is_empty());
}
#[test]
fn test_agent_with_metadata() {
let mut metadata = HashMap::new();
metadata.insert(
"name".to_string(),
serde_json::Value::String("Example Exchange".to_string()),
);
let agent = Agent::with_metadata(
"did:web:example.com",
"Exchange",
"did:example:alice",
metadata,
);
assert_eq!(
agent.get_metadata("name").unwrap().as_str().unwrap(),
"Example Exchange"
);
}
#[test]
fn test_agent_with_policies() {
use crate::message::policy::{Policy, RequireAuthorization};
let auth_req = RequireAuthorization {
from: Some(vec!["did:example:kyc".to_string()]),
from_role: None,
from_agent: None,
purpose: Some("KYC verification".to_string()),
};
let policy = Policy::RequireAuthorization(auth_req);
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_policies(vec![policy]);
assert!(agent.policies.is_some());
assert_eq!(agent.policies.as_ref().unwrap().len(), 1);
}
#[test]
fn test_agent_serialization() {
let agent = Agent::new(
"did:web:example.com",
"SettlementAddress",
"did:example:alice",
)
.with_metadata_field(
"name".to_string(),
serde_json::Value::String("Test Agent".to_string()),
);
let json = serde_json::to_string(&agent).unwrap();
let deserialized: Agent = serde_json::from_str(&json).unwrap();
assert_eq!(agent, deserialized);
assert_eq!(deserialized.role, Some("SettlementAddress".to_string()));
assert_eq!(deserialized.for_parties.0, vec!["did:example:alice"]);
}
#[test]
fn test_agent_json_ld_format() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice");
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["@id"], "did:web:example.com");
assert_eq!(json["role"], "Exchange");
assert_eq!(json["for"], "did:example:alice"); }
#[test]
fn test_agent_helper_methods() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice");
assert!(agent.has_role("Exchange"));
assert!(!agent.has_role("Wallet"));
assert!(agent.acts_for("did:example:alice"));
assert!(!agent.acts_for("did:example:bob"));
}
#[test]
fn test_agent_roles_constants() {
assert_eq!(roles::SETTLEMENT_ADDRESS, "SettlementAddress");
assert_eq!(roles::SOURCE_ADDRESS, "SourceAddress");
assert_eq!(roles::EXCHANGE, "Exchange");
}
#[test]
fn test_agent_multiple_parties() {
let parties = vec![
"did:example:alice".to_string(),
"did:example:bob".to_string(),
];
let agent = Agent::new_for_parties("did:web:example.com", "Exchange", parties.clone());
assert_eq!(agent.for_parties.0, parties);
assert!(agent.acts_for("did:example:alice"));
assert!(agent.acts_for("did:example:bob"));
assert!(!agent.acts_for("did:example:charlie"));
}
#[test]
fn test_agent_for_parties_serialization_single() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice");
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["for"], "did:example:alice");
let deserialized: Agent = serde_json::from_value(json).unwrap();
assert_eq!(deserialized.for_parties.0, vec!["did:example:alice"]);
}
#[test]
fn test_agent_for_parties_serialization_multiple() {
let parties = vec![
"did:example:alice".to_string(),
"did:example:bob".to_string(),
];
let agent = Agent::new_for_parties("did:web:example.com", "Exchange", parties.clone());
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(
json["for"],
serde_json::Value::Array(vec![
serde_json::Value::String("did:example:alice".to_string()),
serde_json::Value::String("did:example:bob".to_string())
])
);
let deserialized: Agent = serde_json::from_value(json).unwrap();
assert_eq!(deserialized.for_parties.0, parties);
}
#[test]
fn test_agent_for_parties_deserialization_from_string() {
let json = serde_json::json!({
"@id": "did:web:example.com",
"role": "Exchange",
"for": "did:example:alice"
});
let agent: Agent = serde_json::from_value(json).unwrap();
assert_eq!(agent.for_parties.0, vec!["did:example:alice"]);
}
#[test]
fn test_agent_for_parties_deserialization_from_array() {
let json = serde_json::json!({
"@id": "did:web:example.com",
"role": "Exchange",
"for": ["did:example:alice", "did:example:bob"]
});
let agent: Agent = serde_json::from_value(json).unwrap();
assert_eq!(
agent.for_parties.0,
vec!["did:example:alice", "did:example:bob"]
);
}
#[test]
fn test_agent_for_parties_methods() {
let mut agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice");
assert_eq!(agent.for_parties(), &["did:example:alice"]);
assert_eq!(agent.primary_party(), Some("did:example:alice"));
agent.add_for_party("did:example:bob");
assert_eq!(
agent.for_parties(),
&["did:example:alice", "did:example:bob"]
);
agent.set_for_parties(vec!["did:example:charlie".to_string()]);
assert_eq!(agent.for_parties(), &["did:example:charlie"]);
assert_eq!(agent.primary_party(), Some("did:example:charlie"));
}
#[test]
fn test_agent_with_name_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_name("Example Exchange Inc.");
assert_eq!(agent.name(), Some("Example Exchange Inc."));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["name"], "Example Exchange Inc.");
let deserialized: Agent = serde_json::from_value(json).unwrap();
assert_eq!(deserialized.name(), Some("Example Exchange Inc."));
}
#[test]
fn test_agent_with_url_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_url("https://example.com");
assert_eq!(agent.url(), Some("https://example.com"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["url"], "https://example.com");
}
#[test]
fn test_agent_with_logo_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_logo("https://example.com/logo.png");
assert_eq!(agent.logo(), Some("https://example.com/logo.png"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["logo"], "https://example.com/logo.png");
}
#[test]
fn test_agent_with_description_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_description("A leading cryptocurrency exchange");
assert_eq!(
agent.description(),
Some("A leading cryptocurrency exchange")
);
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["description"], "A leading cryptocurrency exchange");
}
#[test]
fn test_agent_with_email_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_email("support@example.com");
assert_eq!(agent.email(), Some("support@example.com"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["email"], "support@example.com");
}
#[test]
fn test_agent_with_telephone_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_telephone("+1-555-0100");
assert_eq!(agent.telephone(), Some("+1-555-0100"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["telephone"], "+1-555-0100");
}
#[test]
fn test_agent_with_service_url_field() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_service_url("https://example.com/didcomm");
assert_eq!(agent.service_url(), Some("https://example.com/didcomm"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["serviceUrl"], "https://example.com/didcomm");
}
#[test]
fn test_agent_with_multiple_organization_fields() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_name("Example Exchange Inc.")
.with_url("https://example.com")
.with_logo("https://example.com/logo.png")
.with_description("A leading cryptocurrency exchange")
.with_email("support@example.com")
.with_telephone("+1-555-0100")
.with_service_url("https://example.com/didcomm");
assert_eq!(agent.name(), Some("Example Exchange Inc."));
assert_eq!(agent.url(), Some("https://example.com"));
assert_eq!(agent.logo(), Some("https://example.com/logo.png"));
assert_eq!(
agent.description(),
Some("A leading cryptocurrency exchange")
);
assert_eq!(agent.email(), Some("support@example.com"));
assert_eq!(agent.telephone(), Some("+1-555-0100"));
assert_eq!(agent.service_url(), Some("https://example.com/didcomm"));
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["@id"], "did:web:example.com");
assert_eq!(json["role"], "Exchange");
assert_eq!(json["for"], "did:example:alice");
assert_eq!(json["name"], "Example Exchange Inc.");
assert_eq!(json["url"], "https://example.com");
assert_eq!(json["logo"], "https://example.com/logo.png");
assert_eq!(json["description"], "A leading cryptocurrency exchange");
assert_eq!(json["email"], "support@example.com");
assert_eq!(json["telephone"], "+1-555-0100");
assert_eq!(json["serviceUrl"], "https://example.com/didcomm");
let deserialized: Agent = serde_json::from_value(json).unwrap();
assert_eq!(deserialized.name(), Some("Example Exchange Inc."));
assert_eq!(deserialized.url(), Some("https://example.com"));
assert_eq!(
deserialized.service_url(),
Some("https://example.com/didcomm")
);
}
#[test]
fn test_agent_json_ld_compliance_with_organization_fields() {
let agent = Agent::new("did:web:example.com", "Exchange", "did:example:alice")
.with_name("Example Exchange")
.with_metadata_field(
"lei:leiCode".to_string(),
serde_json::Value::String("123456789012345678".to_string()),
);
let json = serde_json::to_value(&agent).unwrap();
assert_eq!(json["@id"], "did:web:example.com");
assert_eq!(json["name"], "Example Exchange");
assert_eq!(json["lei:leiCode"], "123456789012345678");
assert!(json.get("metadata").is_none());
}
}