use crate::web5::identity::{Web5Error, Web5Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
pub trait ProtocolHandler: Send + Sync {
fn protocol_id(&self) -> &str;
fn handle_message(&self, message: &[u8]) -> Web5Result<Vec<u8>>;
fn get_definition(&self) -> ProtocolDefinition;
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ProtocolDefinition {
pub protocol: String,
pub version: String,
pub types: HashMap<String, TypeDefinition>,
pub actions: Vec<ActionDefinition>,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct TypeDefinition {
pub schema: String,
pub description: String,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ActionDefinition {
pub name: String,
pub description: String,
pub input: Option<String>,
pub output: Option<String>,
}
pub struct ProtocolManager {
protocols: HashMap<String, ProtocolDefinition>,
handlers: HashMap<String, Box<dyn ProtocolHandler>>,
}
impl Default for ProtocolManager {
fn default() -> Self {
Self::new()
}
}
impl ProtocolManager {
pub fn new() -> Self {
Self {
protocols: HashMap::new(),
handlers: HashMap::new(),
}
}
pub fn register_protocol(&mut self, handler: Box<dyn ProtocolHandler>) -> Web5Result<()> {
let protocol_id = handler.protocol_id().to_string();
let definition = handler.get_definition();
self.protocols.insert(protocol_id.clone(), definition);
self.handlers.insert(protocol_id, handler);
Ok(())
}
pub fn get_protocol(&self, protocol_id: &str) -> Web5Result<&ProtocolDefinition> {
self.protocols
.get(protocol_id)
.ok_or_else(|| Web5Error::Protocol(format!("Protocol not found: {protocol_id}")))
}
pub fn handle_message(&self, protocol_id: &str, message: &[u8]) -> Web5Result<Vec<u8>> {
let handler = self.handlers.get(protocol_id).ok_or_else(|| {
Web5Error::Protocol(format!("No handler found for protocol: {protocol_id}"))
})?;
handler.handle_message(message)
}
pub fn has_protocol(&self, protocol_id: &str) -> bool {
self.protocols.contains_key(protocol_id)
}
pub fn get_all_protocols(&self) -> Vec<&ProtocolDefinition> {
self.protocols.values().collect()
}
}
pub struct ProfileProtocolHandler;
impl Default for ProfileProtocolHandler {
fn default() -> Self {
Self::new()
}
}
impl ProfileProtocolHandler {
pub fn new() -> Self {
Self {}
}
}
impl ProtocolHandler for ProfileProtocolHandler {
fn protocol_id(&self) -> &str {
"https://identity.foundation/schemas/profile"
}
fn handle_message(&self, message: &[u8]) -> Web5Result<Vec<u8>> {
Ok(message.to_vec())
}
fn get_definition(&self) -> ProtocolDefinition {
let mut types = HashMap::new();
types.insert(
"profile".to_string(),
TypeDefinition {
schema: r#"{
"type": "object",
"properties": {
"name": { "type": "string" },
"image": { "type": "string", "format": "uri" },
"description": { "type": "string" }
}
}"#
.to_string(),
description: "A user profile".to_string(),
},
);
let actions = vec![
ActionDefinition {
name: "get".to_string(),
description: "Get a profile".to_string(),
input: None,
output: Some("profile".to_string()),
},
ActionDefinition {
name: "update".to_string(),
description: "Update a profile".to_string(),
input: Some("profile".to_string()),
output: Some("profile".to_string()),
},
];
ProtocolDefinition {
protocol: self.protocol_id().to_string(),
version: "1.0".to_string(),
types,
actions,
}
}
}
pub struct CredentialProtocolHandler;
impl Default for CredentialProtocolHandler {
fn default() -> Self {
Self::new()
}
}
impl CredentialProtocolHandler {
pub fn new() -> Self {
Self {}
}
}
impl ProtocolHandler for CredentialProtocolHandler {
fn protocol_id(&self) -> &str {
"https://identity.foundation/schemas/credentials"
}
fn handle_message(&self, message: &[u8]) -> Web5Result<Vec<u8>> {
Ok(message.to_vec())
}
fn get_definition(&self) -> ProtocolDefinition {
let mut types = HashMap::new();
types.insert(
"credential".to_string(),
TypeDefinition {
schema: r#"{
"type": "object",
"properties": {
"id": { "type": "string" },
"type": { "type": "array", "items": { "type": "string" } },
"issuer": { "type": "string" },
"issuanceDate": { "type": "string", "format": "date-time" },
"credentialSubject": { "type": "object" }
}
}"#
.to_string(),
description: "A verifiable credential".to_string(),
},
);
let actions = vec![
ActionDefinition {
name: "issue".to_string(),
description: "Issue a credential".to_string(),
input: Some("credential".to_string()),
output: Some("credential".to_string()),
},
ActionDefinition {
name: "verify".to_string(),
description: "Verify a credential".to_string(),
input: Some("credential".to_string()),
output: None,
},
];
ProtocolDefinition {
protocol: self.protocol_id().to_string(),
version: "1.0".to_string(),
types,
actions,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[tokio::test]
async fn test_protocol_manager() -> Result<(), Box<dyn Error>> {
let mut manager = ProtocolManager::new();
let profile_handler = Box::new(ProfileProtocolHandler::new());
manager.register_protocol(profile_handler)?;
assert!(manager.has_protocol("https://identity.foundation/schemas/profile"));
let protocols = manager.get_all_protocols();
assert_eq!(protocols.len(), 1);
let _def = manager.get_protocol("https://identity.foundation/schemas/profile")?;
Ok(())
}
#[tokio::test]
async fn test_profile_protocol_handler() -> Result<(), Box<dyn Error>> {
let handler = ProfileProtocolHandler::new();
assert_eq!(
handler.protocol_id(),
"https://identity.foundation/schemas/profile"
);
let response = handler.handle_message(b"test")?;
assert_eq!(response, b"test");
Ok(())
}
#[tokio::test]
async fn test_credential_protocol_handler() -> Result<(), Box<dyn Error>> {
let handler = CredentialProtocolHandler::new();
assert_eq!(
handler.protocol_id(),
"https://identity.foundation/schemas/credentials"
);
let response = handler.handle_message(b"test")?;
assert_eq!(response, b"test");
Ok(())
}
}