use crate::action::ActionEnvelope;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::borrow::Cow;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
pub trait JobSpec {
type Request: Serialize + DeserializeOwned + Send + 'static;
type Ok: Serialize + DeserializeOwned + Send + 'static;
type Err: Serialize + DeserializeOwned + Send + 'static;
const NAME: &'static str;
}
#[derive(Debug)]
pub struct JobRef<J: JobSpec> {
pub name: &'static str,
_marker: PhantomData<fn() -> J>,
}
impl<J: JobSpec> JobRef<J> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_marker: PhantomData,
}
}
}
impl<J: JobSpec> Clone for JobRef<J> {
fn clone(&self) -> Self {
*self
}
}
impl<J: JobSpec> Copy for JobRef<J> {}
pub trait ServiceSpec {
type Config: Serialize + DeserializeOwned + Send + 'static;
type Command: Serialize + DeserializeOwned + Send + 'static;
type CommandOk: Serialize + DeserializeOwned + Send + 'static;
type CommandErr: Serialize + DeserializeOwned + Send + 'static;
type Event: Serialize + DeserializeOwned + Send + 'static;
type StartErr: Serialize + DeserializeOwned + Send + 'static;
const NAME: &'static str;
}
#[derive(Debug)]
pub struct ServiceType<S: ServiceSpec> {
pub name: &'static str,
_marker: PhantomData<fn() -> S>,
}
impl<S: ServiceSpec> ServiceType<S> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_marker: PhantomData,
}
}
}
impl<S: ServiceSpec> Clone for ServiceType<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S: ServiceSpec> Copy for ServiceType<S> {}
#[derive(Debug)]
pub struct ServiceSlot<S: ServiceSpec> {
pub ty: ServiceType<S>,
pub slot_key: Cow<'static, str>,
}
impl<S: ServiceSpec> ServiceSlot<S> {
pub fn singleton(ty: ServiceType<S>) -> Self {
Self {
ty,
slot_key: Cow::Borrowed("singleton"),
}
}
pub fn keyed(ty: ServiceType<S>, key: impl Into<String>) -> Self {
Self {
ty,
slot_key: Cow::Owned(key.into()),
}
}
pub fn slot_key(&self) -> &str {
self.slot_key.as_ref()
}
}
impl<S: ServiceSpec> Clone for ServiceSlot<S> {
fn clone(&self) -> Self {
Self {
ty: self.ty,
slot_key: self.slot_key.clone(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct JobRequestPayload {
pub job_name: String,
pub payload: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ServiceStartPayload {
pub service_name: String,
pub slot_key: String,
pub config: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ServiceCommandPayload {
pub service_name: String,
pub slot_key: String,
pub payload: Vec<u8>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ServiceStopPayload {
pub service_name: String,
pub slot_key: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct ServiceBindings {
pub on_started: Option<ActionEnvelope>,
pub on_start_failed: Option<ActionEnvelope>,
pub on_event: Option<ActionEnvelope>,
pub on_stopped: Option<ActionEnvelope>,
pub on_command_ok: Option<ActionEnvelope>,
pub on_command_err: Option<ActionEnvelope>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ResourceExecutionContext {
pub key: String,
pub generation: u64,
}
#[derive(Clone, Debug)]
pub struct JobCtx {
pub req_id: u64,
}
type EmitFn = dyn Fn(Vec<u8>) -> BoxFuture<Result<(), String>> + Send + Sync;
struct ServiceCtxInner {
service_name: String,
slot_key: String,
instance_id: u64,
emit: Arc<EmitFn>,
}
pub struct ServiceCtx<S: ServiceSpec> {
inner: Arc<ServiceCtxInner>,
_marker: PhantomData<fn() -> S>,
}
impl<S: ServiceSpec> std::fmt::Debug for ServiceCtx<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ServiceCtx")
.field("service_name", &self.inner.service_name)
.field("slot_key", &self.inner.slot_key)
.field("instance_id", &self.inner.instance_id)
.finish()
}
}
impl<S: ServiceSpec> Clone for ServiceCtx<S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_marker: PhantomData,
}
}
}
impl<S: ServiceSpec> ServiceCtx<S> {
#[doc(hidden)]
pub fn new_runtime(
service_name: String,
slot_key: String,
instance_id: u64,
emit: Arc<EmitFn>,
) -> Self {
Self {
inner: Arc::new(ServiceCtxInner {
service_name,
slot_key,
instance_id,
emit,
}),
_marker: PhantomData,
}
}
pub fn service_name(&self) -> &str {
&self.inner.service_name
}
pub fn slot_key(&self) -> &str {
&self.inner.slot_key
}
pub fn instance_id(&self) -> u64 {
self.inner.instance_id
}
pub fn emit(&self, event: S::Event) -> BoxFuture<Result<(), String>> {
match serde_json::to_vec(&event) {
Ok(bytes) => (self.inner.emit)(bytes),
Err(err) => Box::pin(async move { Err(err.to_string()) }),
}
}
}
pub trait ServiceRunner<S: ServiceSpec>: Send + 'static {
fn on_command(
&mut self,
command: S::Command,
ctx: ServiceCtx<S>,
) -> BoxFuture<Result<S::CommandOk, S::CommandErr>>;
fn on_stop(self: Box<Self>, ctx: ServiceCtx<S>) -> BoxFuture<()>;
}