use crate::agents::env::{agent_id, executor};
use crate::common::Id;
use crate::contracts::env::{contract_id, is_contract, participant, sinks};
use crate::events::private::Sealed;
use anyhow::anyhow;
use borderless_id_types::{BorderlessId, ContractId};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::{fmt::Debug, fmt::Display, str::FromStr};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum MethodOrId {
ByName { method: String },
ById { method_id: u32 },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CallAction {
#[serde(flatten)]
pub method: MethodOrId,
pub params: Value,
}
impl FromStr for CallAction {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
impl CallAction {
pub fn new(method: MethodOrId, params: Value) -> Self {
Self { method, params }
}
pub fn by_method(method_name: impl AsRef<str>, params: Value) -> Self {
Self {
method: MethodOrId::ByName {
method: method_name.as_ref().to_string(),
},
params,
}
}
pub fn by_method_id(method_id: u32, params: Value) -> Self {
Self {
method: MethodOrId::ById { method_id },
params,
}
}
pub fn method_name(&self) -> Option<&str> {
match &self.method {
MethodOrId::ByName { method } => Some(method.as_str()),
MethodOrId::ById { .. } => None,
}
}
pub fn method_id(&self) -> Option<u32> {
match self.method {
MethodOrId::ByName { .. } => None,
MethodOrId::ById { method_id } => Some(method_id),
}
}
pub fn print_method(&self) -> String {
match &self.method {
MethodOrId::ByName { method } => format!("method-name={method}"),
MethodOrId::ById { method_id } => format!("method-id={method_id}"),
}
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_slice(bytes)
}
pub fn pretty_print(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(&self)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
serde_json::to_vec(&self)
}
}
pub struct CBInit;
pub struct CBWithAction;
pub struct CallBuilder<STATE> {
pub(crate) id: ContractId,
pub(crate) name: String,
pub(crate) writer: Option<BorderlessId>,
pub(crate) action: Option<CallAction>,
_marker: std::marker::PhantomData<STATE>,
}
impl CallBuilder<CBInit> {
pub fn new(id: ContractId, method_name: &str) -> CallBuilder<CBInit> {
CallBuilder {
id,
name: method_name.to_string(),
writer: None,
action: None,
_marker: std::marker::PhantomData,
}
}
pub(crate) fn new_with_writer(
id: ContractId,
method_name: &str,
writer: &str,
) -> CallBuilder<CBInit> {
let writer = if is_contract() {
participant(writer).expect("sink contains unknown writer")
} else {
executor()
};
CallBuilder {
id,
name: method_name.to_string(),
writer: Some(writer),
action: None,
_marker: std::marker::PhantomData,
}
}
pub fn with_value(self, value: Value) -> CallBuilder<CBWithAction> {
let action = CallAction::by_method(&self.name, value);
CallBuilder {
id: self.id,
name: self.name,
writer: self.writer,
action: Some(action),
_marker: std::marker::PhantomData,
}
}
pub fn with_args<T: serde::Serialize>(
self,
args: T,
) -> Result<CallBuilder<CBWithAction>, crate::Error> {
let value = serde_json::to_value(args).map_err(|e| {
crate::Error::msg(format!("failed to convert args for method-call: {e}"))
})?;
let action = CallAction::by_method(&self.name, value);
Ok(CallBuilder {
id: self.id,
name: self.name,
writer: self.writer,
action: Some(action),
_marker: std::marker::PhantomData,
})
}
}
impl CallBuilder<CBWithAction> {
pub fn with_writer(
self,
writer_alias: impl AsRef<str>,
) -> Result<CallBuilder<CBWithAction>, crate::Error> {
let writer_id = participant(writer_alias.as_ref())?;
Ok(CallBuilder {
id: self.id,
name: self.name,
writer: Some(writer_id),
action: self.action,
_marker: std::marker::PhantomData,
})
}
pub fn build(self) -> Result<ContractCall, crate::Error> {
debug_assert!(self.action.is_some(), "invariant: action must be set");
if let Some(writer) = self.writer {
return Ok(ContractCall {
contract_id: self.id,
action: self.action.unwrap(),
writer,
});
}
let mut sinks: Vec<Sink> = sinks()
.into_iter()
.filter(|s| s.contract_id == self.id)
.collect();
let writer = match sinks.len() {
0 => return Err(anyhow!("Found no sink related to contract-id {}", self.id)),
1 => {
let sink = sinks.pop().unwrap();
participant(sink.writer)?
}
_ => {
return Err(anyhow!(
"Found multiple sinks for contract-id {} - please specify the writer directly",
self.id
));
}
};
Ok(ContractCall {
contract_id: self.id,
action: self.action.unwrap(),
writer,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractCall {
pub contract_id: ContractId,
pub action: CallAction,
pub writer: BorderlessId,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
pub publisher: Id,
pub topic: String,
pub value: Value,
}
pub fn message(topic: impl AsRef<str>) -> MsgBuilder {
let publisher = if is_contract() {
Id::contract(contract_id())
} else {
Id::agent(agent_id())
};
MsgBuilder {
publisher,
topic: topic.as_ref().to_ascii_lowercase(),
}
}
pub struct MsgBuilder {
publisher: Id,
topic: String,
}
impl MsgBuilder {
pub fn with_value(self, value: Value) -> Message {
Message {
publisher: self.publisher,
topic: self.topic,
value,
}
}
pub fn with_content<T: serde::Serialize>(self, content: &T) -> Result<Message, crate::Error> {
let value = serde_json::to_value(content).map_err(|e| {
crate::Error::msg(format!(
"failed to serialize argument for message on topic '{}': {e}",
self.topic,
))
})?;
Ok(Message {
publisher: self.publisher,
topic: self.topic,
value,
})
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Events {
pub contracts: Vec<ContractCall>,
pub local: Vec<Message>,
}
impl Events {
pub fn is_empty(&self) -> bool {
self.contracts.is_empty() && self.local.is_empty()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
serde_json::from_slice(bytes)
}
pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
serde_json::to_vec(self)
}
}
impl From<ContractCall> for Events {
fn from(value: ContractCall) -> Self {
Events {
contracts: vec![value],
local: Vec::new(),
}
}
}
impl From<Message> for Events {
fn from(value: Message) -> Self {
Events {
contracts: Vec::new(),
local: vec![value],
}
}
}
impl From<Vec<ContractCall>> for Events {
fn from(value: Vec<ContractCall>) -> Self {
Events {
contracts: value,
local: Vec::new(),
}
}
}
impl From<Vec<Message>> for Events {
fn from(value: Vec<Message>) -> Self {
Events {
contracts: Vec::new(),
local: value,
}
}
}
pub trait ActionOutput: Sealed {
fn convert_out_events(self) -> crate::Result<Events>;
}
mod private {
pub trait Sealed {}
}
impl Sealed for () {}
impl ActionOutput for () {
fn convert_out_events(self) -> crate::Result<Events> {
Ok(Events::default())
}
}
impl<E> Sealed for Result<(), E> where E: Display + Send + Sync + 'static {}
impl<E> ActionOutput for Result<(), E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> crate::Result<Events> {
self.map_err(|e| crate::Error::msg(e))?.convert_out_events()
}
}
impl Sealed for Events {}
impl ActionOutput for Events {
fn convert_out_events(self) -> anyhow::Result<Events> {
Ok(self)
}
}
impl<E> Sealed for Result<Events, E> where E: Display + Debug + Send + Sync + 'static {}
impl<E> ActionOutput for Result<Events, E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> anyhow::Result<Events> {
let inner = self.map_err(|e| crate::Error::msg(e))?;
inner.convert_out_events()
}
}
impl Sealed for ContractCall {}
impl ActionOutput for ContractCall {
fn convert_out_events(self) -> crate::Result<Events> {
Ok(Events::from(self))
}
}
impl<E> Sealed for Result<ContractCall, E> where E: Display + Debug + Send + Sync + 'static {}
impl<E> ActionOutput for Result<ContractCall, E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> crate::Result<Events> {
let inner = self.map_err(|e| crate::Error::msg(e))?;
inner.convert_out_events()
}
}
impl Sealed for Vec<ContractCall> {}
impl ActionOutput for Vec<ContractCall> {
fn convert_out_events(self) -> anyhow::Result<Events> {
Ok(Events::from(self))
}
}
impl<E> Sealed for Result<Vec<ContractCall>, E> where E: Display + Debug + Send + Sync + 'static {}
impl<E> ActionOutput for Result<Vec<ContractCall>, E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> anyhow::Result<Events> {
let inner = self.map_err(|e| crate::Error::msg(e))?;
inner.convert_out_events()
}
}
impl Sealed for Message {}
impl ActionOutput for Message {
fn convert_out_events(self) -> anyhow::Result<Events> {
Ok(Events::from(self))
}
}
impl<E> Sealed for Result<Message, E> where E: Display + Debug + Send + Sync + 'static {}
impl<E> ActionOutput for Result<Message, E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> anyhow::Result<Events> {
let inner = self.map_err(|e| crate::Error::msg(e))?;
inner.convert_out_events()
}
}
impl Sealed for Vec<Message> {}
impl ActionOutput for Vec<Message> {
fn convert_out_events(self) -> anyhow::Result<Events> {
Ok(Events::from(self))
}
}
impl<E> Sealed for Result<Vec<Message>, E> where E: Display + Debug + Send + Sync + 'static {}
impl<E> ActionOutput for Result<Vec<Message>, E>
where
E: Display + Debug + Send + Sync + 'static,
{
fn convert_out_events(self) -> anyhow::Result<Events> {
let inner = self.map_err(|e| crate::Error::msg(e))?;
inner.convert_out_events()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Sink {
pub contract_id: ContractId,
pub alias: String,
pub writer: String,
}
impl Sink {
pub fn new(contract_id: ContractId, alias: String, writer: String) -> Sink {
Sink {
contract_id,
alias,
writer,
}
}
pub fn has_alias(&self, alias: impl AsRef<str>) -> bool {
alias.as_ref().eq_ignore_ascii_case(&self.alias)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Topic {
pub publisher: Id,
pub topic: String,
pub method: String,
}
impl Topic {
pub fn new(publisher: Id, topic: String, method: String) -> Self {
Topic {
publisher,
topic,
method,
}
}
}