use std::sync::Arc;
use std::time::Duration;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::DBOS;
use crate::checkpoint::{Built, PendingStep, StepPlacement, StepWriter};
use crate::connection::Connection;
use crate::context::Ctx;
use crate::error::{DurableError, Error, Result};
use crate::serialization::SerializationFormat;
use crate::serialization::{PayloadEncoding, decode_tagged, encode};
use crate::sysdb::types::{GetEventCaller, step_names};
pub fn set_event<'a, T, E>(key: &'a str, value: &T) -> PendingStep<'a, (), E>
where
T: Serialize,
E: DurableError + 'a,
{
set_event_with(key, value, SetEventOptions::default())
}
pub fn set_event_with<'a, T, E>(
key: &'a str,
value: &T,
options: SetEventOptions,
) -> PendingStep<'a, (), E>
where
T: Serialize,
E: DurableError + 'a,
{
let built = place_set_event(value, options.serialization);
PendingStep::placed(
step_names::SET_EVENT,
built,
move |(executor, encoded, encoding), placement| async move {
let Some(StepWriter {
workflow_id,
step_id,
owner_xid,
}) = placement.step_writer()
else {
unreachable!("set_event is refused outside a workflow and inside a step")
};
executor
.sysdb()
.set_event(
workflow_id,
step_id,
key,
&encoded,
Some(encoding.name()),
owner_xid,
)
.await
.map_err(Error::from_execution)?;
Ok(())
},
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SetEventOptions {
pub serialization: SerializationFormat,
}
fn place_set_event<T: Serialize>(
value: &T,
serialization: SerializationFormat,
) -> Built<(Arc<crate::Executor>, String, PayloadEncoding)> {
let Some(ctx) = Ctx::current() else {
return Err(Error::NotInWorkflow {
operation: "set_event".into(),
});
};
if ctx.in_step() {
return Err(Error::InsideStep {
operation: "set_event".into(),
});
}
let encoding = PayloadEncoding::resolve(serialization, ctx.encoding());
let encoded = encode(value, &encoding, "event value")?;
let executor = Arc::clone(ctx.executor());
Ok(((executor, encoded, encoding), StepPlacement::at(ctx)))
}
pub fn get_event<'a, T, E>(
workflow_id: &'a str,
key: &'a str,
timeout: Duration,
) -> PendingStep<'a, Option<T>, E>
where
T: DeserializeOwned + 'a,
E: DurableError + 'a,
{
let built = Ctx::current()
.ok_or(Error::NotInWorkflow {
operation: "get_event".into(),
})
.map(|ctx| {
let executor = Arc::clone(ctx.executor());
let placement = StepPlacement::at(ctx);
let timeout_step_id = placement.next_step_id();
((executor, timeout_step_id), placement)
});
pending_get_event(built, workflow_id, key, timeout)
}
impl DBOS {
pub fn get_event<'a, T: DeserializeOwned + 'a>(
&self,
workflow_id: &'a str,
key: &'a str,
timeout: Duration,
) -> PendingStep<'a, Option<T>> {
let built = StepPlacement::taken(self.executor("get_event"), "get_event").map(
|(executor, placement)| {
let timeout_step_id = placement.next_step_id();
((executor, timeout_step_id), placement)
},
);
pending_get_event(built, workflow_id, key, timeout)
}
}
impl crate::Client {
pub async fn get_event<T: DeserializeOwned>(
&self,
workflow_id: &str,
key: &str,
timeout: Duration,
) -> Result<Option<T>> {
self.connection()
.get_event(workflow_id, key, timeout, None)
.await
}
}
fn pending_get_event<'a, T, E>(
built: Built<(Arc<crate::Executor>, Option<i32>)>,
workflow_id: &'a str,
key: &'a str,
timeout: Duration,
) -> PendingStep<'a, Option<T>, E>
where
T: DeserializeOwned + 'a,
E: DurableError + 'a,
{
PendingStep::placed(
step_names::GET_EVENT,
built,
move |(executor, timeout_step_id), placement| async move {
let caller =
placement
.step_writer()
.zip(timeout_step_id)
.map(|(caller, timeout_step_id)| GetEventCaller {
workflow_id: caller.workflow_id,
step_id: caller.step_id,
timeout_step_id,
owner_xid: caller.owner_xid,
});
executor
.connection()
.get_event(workflow_id, key, timeout, caller)
.await
},
)
}
impl Connection {
pub(crate) async fn get_event<T: DeserializeOwned, E>(
&self,
workflow_id: &str,
key: &str,
timeout: Duration,
caller: Option<GetEventCaller<'_>>,
) -> Result<Option<T>, E> {
match self
.sysdb()
.get_event(workflow_id, key, timeout, caller)
.await
.map_err(Error::from_execution)?
{
None => Ok(None),
Some(found) => decode_tagged(
Some(&found.value),
found.serialization.as_deref(),
self.serializer(),
"event value",
)
.map(Some),
}
}
}