use std::{
fmt,
sync::Arc,
task::{Context, Poll},
};
use futures_util::future::{BoxFuture, poll_fn};
use tokio::sync::Mutex;
use tower_service::Service;
use crate::{
context::TaskContext,
error::{Error, Result},
types::{Json, JsonObject, RunId, TaskId},
worker::ErasedTaskExecutor,
};
pub struct ExecutionRequest {
context: TaskContext,
input: Json,
executor: ErasedTaskExecutor,
}
impl ExecutionRequest {
pub(crate) fn new(context: TaskContext, input: Json, executor: ErasedTaskExecutor) -> Self {
Self { context, input, executor }
}
pub fn task_id(&self) -> TaskId {
self.context.task_id()
}
pub fn run_id(&self) -> RunId {
self.context.run_id()
}
pub fn task_name(&self) -> &str {
self.context.task_name()
}
pub fn queue_name(&self) -> &str {
self.context.queue_name()
}
pub fn attempt(&self) -> u32 {
self.context.attempt()
}
pub fn headers(&self) -> &JsonObject {
self.context.headers()
}
pub const fn context(&self) -> &TaskContext {
&self.context
}
pub(crate) fn into_parts(self) -> (TaskContext, Json, ErasedTaskExecutor) {
(self.context, self.input, self.executor)
}
}
impl fmt::Debug for ExecutionRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExecutionRequest")
.field("task_id", &self.task_id())
.field("run_id", &self.run_id())
.field("task_name", &self.task_name())
.field("queue_name", &self.queue_name())
.field("attempt", &self.attempt())
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct ExecutionResponse {
output: Json,
}
impl ExecutionResponse {
const fn new(output: Json) -> Self {
Self { output }
}
pub(crate) fn into_output(self) -> Json {
self.output
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ExecutionService;
impl Service<ExecutionRequest> for ExecutionService {
type Response = ExecutionResponse;
type Error = Error;
type Future = BoxFuture<'static, Result<Self::Response>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<()>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, request: ExecutionRequest) -> Self::Future {
Box::pin(async move {
let (context, input, executor) = request.into_parts();
let output = executor(input, context).await?;
Ok(ExecutionResponse::new(output))
})
}
}
#[derive(Clone)]
pub(crate) struct SharedExecutionService {
call: Arc<
dyn Fn(ExecutionRequest) -> BoxFuture<'static, Result<ExecutionResponse>> + Send + Sync,
>,
}
impl fmt::Debug for SharedExecutionService {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SharedExecutionService").finish_non_exhaustive()
}
}
impl SharedExecutionService {
pub(crate) fn new<S>(service: S) -> Self
where
S: Service<ExecutionRequest, Response = ExecutionResponse, Error = Error> + Send + 'static,
S::Future: Send + 'static,
{
let service = Arc::new(Mutex::new(service));
Self {
call: Arc::new(move |request| {
let service = Arc::clone(&service);
Box::pin(async move {
let mut service = service.lock().await;
poll_fn(|cx| service.poll_ready(cx)).await?;
let response = service.call(request);
drop(service);
response.await
})
}),
}
}
}
impl Service<ExecutionRequest> for SharedExecutionService {
type Response = ExecutionResponse;
type Error = Error;
type Future = BoxFuture<'static, Result<Self::Response>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<()>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, request: ExecutionRequest) -> Self::Future {
(self.call)(request)
}
}