use async_trait::async_trait;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::fmt::Debug;
use crate::effects::{LabelId, RoleId};
use crate::identifiers::RoleName;
pub trait Message: Serialize + DeserializeOwned + Send + Sync + Debug + 'static {}
impl<T: Serialize + DeserializeOwned + Send + Sync + Debug + 'static> Message for T {}
#[async_trait]
pub trait ChoreographicAdapter: Send {
type Error: std::error::Error + Send + Sync + 'static;
type Role: RoleId;
async fn send<M: Message>(&mut self, to: Self::Role, msg: M) -> Result<(), Self::Error>;
async fn recv<M: Message>(&mut self, from: Self::Role) -> Result<M, Self::Error>;
async fn broadcast<M: Message + Clone>(
&mut self,
to: &[Self::Role],
msg: M,
) -> Result<(), Self::Error> {
for role in to {
self.send(*role, msg.clone()).await?;
}
Ok(())
}
async fn collect<M: Message>(&mut self, from: &[Self::Role]) -> Result<Vec<M>, Self::Error> {
let mut messages = Vec::with_capacity(from.len());
for role in from {
let msg = self.recv::<M>(*role).await?;
messages.push(msg);
}
Ok(messages)
}
async fn choose(
&mut self,
to: Self::Role,
label: <Self::Role as RoleId>::Label,
) -> Result<(), Self::Error> {
self.send(to, ChoiceLabel(label)).await
}
async fn offer(
&mut self,
from: Self::Role,
) -> Result<<Self::Role as RoleId>::Label, Self::Error> {
let choice: ChoiceLabel<<Self::Role as RoleId>::Label> = self.recv(from).await?;
Ok(choice.0)
}
fn resolve_family(&self, family: &str) -> Result<Vec<Self::Role>, Self::Error>;
fn resolve_range(
&self,
family: &str,
start: u32,
end: u32,
) -> Result<Vec<Self::Role>, Self::Error>;
fn family_size(&self, family: &str) -> Result<usize, Self::Error> {
self.resolve_family(family).map(|v| v.len())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ChoiceLabel<L: LabelId>(pub L);
impl<L: LabelId> Serialize for ChoiceLabel<L> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.0.as_str())
}
}
impl<'de, L: LabelId> Deserialize<'de> for ChoiceLabel<L> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let label = String::deserialize(deserializer)?;
L::from_str(&label)
.map(ChoiceLabel)
.ok_or_else(|| serde::de::Error::custom("Unknown choice label"))
}
}
#[async_trait]
pub trait ChoreographicAdapterExt: ChoreographicAdapter {
async fn setup(&mut self) -> Result<(), Self::Error>;
async fn teardown(&mut self) -> Result<(), Self::Error>;
}
#[derive(Debug, Clone)]
pub struct ProtocolContext {
pub protocol: &'static str,
pub role: RoleName,
pub index: Option<u32>,
}
impl ProtocolContext {
#[must_use]
pub fn new(protocol: &'static str, role: RoleName) -> Self {
Self {
protocol,
role,
index: None,
}
}
#[must_use]
pub fn indexed(protocol: &'static str, role: RoleName, index: u32) -> Self {
Self {
protocol,
role,
index: Some(index),
}
}
#[must_use]
pub fn for_role<R: RoleId>(protocol: &'static str, role: R) -> Self {
Self {
protocol,
role: role.role_name(),
index: role.role_index(),
}
}
}
#[derive(Debug)]
pub struct ProtocolOutput<T> {
pub value: T,
pub metadata: Option<ExecutionMetadata>,
}
impl<T> ProtocolOutput<T> {
pub fn new(value: T) -> Self {
Self {
value,
metadata: None,
}
}
pub fn with_metadata(value: T, metadata: ExecutionMetadata) -> Self {
Self {
value,
metadata: Some(metadata),
}
}
}
#[derive(Debug, Default)]
pub struct ExecutionMetadata {
pub messages_sent: usize,
pub messages_received: usize,
pub duration_ms: Option<u64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_role_id_display() {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum TestRole {
Client,
Witness(u32),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum TestLabel {
Ping,
}
impl LabelId for TestLabel {
fn as_str(&self) -> &'static str {
match self {
TestLabel::Ping => "Ping",
}
}
fn from_str(label: &str) -> Option<Self> {
match label {
"Ping" => Some(TestLabel::Ping),
_ => None,
}
}
}
impl RoleId for TestRole {
type Label = TestLabel;
fn role_name(&self) -> RoleName {
match self {
TestRole::Client => RoleName::from_static("Client"),
TestRole::Witness(_) => RoleName::from_static("Witness"),
}
}
fn role_index(&self) -> Option<u32> {
match self {
TestRole::Witness(index) => Some(*index),
_ => None,
}
}
}
let static_role = TestRole::Client;
assert_eq!(static_role.role_name().as_str(), "Client");
let indexed_role = TestRole::Witness(2);
assert_eq!(indexed_role.role_name().as_str(), "Witness");
assert_eq!(indexed_role.role_index(), Some(2));
}
#[test]
fn test_protocol_context() {
let ctx = ProtocolContext::new("TwoBuyer", RoleName::from_static("Buyer1"));
assert_eq!(ctx.protocol, "TwoBuyer");
assert_eq!(ctx.role.as_str(), "Buyer1");
assert!(ctx.index.is_none());
let indexed_ctx =
ProtocolContext::indexed("Broadcast", RoleName::from_static("Witness"), 0);
assert_eq!(indexed_ctx.index, Some(0));
}
}