use std::sync::Arc;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{ConnectorDescription, DispatchResponse, DispatchSession, StepResponse};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecuteResult {
pub result: Option<Value>,
pub error: Option<String>,
pub logs: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum DispatchRequest {
Call {
execution_id: String,
seq: u64,
connector: String,
method: String,
arguments: Value,
},
BeginStep {
execution_id: String,
seq: u64,
name: String,
},
RecordStep {
execution_id: String,
seq: u64,
result: Value,
},
}
#[async_trait(?Send)]
pub trait CodeExecutor {
async fn execute(
&self,
code: &str,
connectors: &[ConnectorDescription],
execution_id: &str,
host: Arc<ExecutionHost>,
) -> Result<ExecuteResult, String>;
}
pub trait Clock: Send + Sync {
fn now_ms(&self) -> u64;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SystemClock;
impl Clock for SystemClock {
fn now_ms(&self) -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
}
pub struct ExecutionHost {
session: Arc<DispatchSession>,
clock: Arc<dyn Clock>,
}
impl ExecutionHost {
pub(crate) fn new(session: Arc<DispatchSession>, clock: Arc<dyn Clock>) -> Self {
Self { session, clock }
}
pub async fn call(
&self,
seq: u64,
connector: &str,
method: &str,
arguments: Value,
) -> DispatchResponse {
self.session
.call_at(seq, connector, method, arguments, self.clock.now_ms())
.await
}
pub async fn begin_step(&self, seq: u64, name: &str) -> Result<StepResponse, String> {
self.session
.begin_step_at(seq, name, self.clock.now_ms())
.await
.map_err(|error| error.to_string())
}
pub async fn record_step(&self, seq: u64, result: Value) -> Result<(), String> {
self.session
.record_step(seq, result, self.clock.now_ms())
.await
.map_err(|error| error.to_string())
}
}