use schemars::JsonSchema;
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::Value;
use super::{WorkflowHandler, input_schema_for};
pub trait TypedWorkflow: WorkflowHandler {
type Input: Serialize + DeserializeOwned + Send;
fn typed_input_schema() -> Option<Value>
where
Self: Sized,
Self::Input: JsonSchema,
{
Some(input_schema_for::<Self::Input>())
}
}
pub fn sub_workflow_names(handlers: &[&dyn WorkflowHandler]) -> Vec<String> {
handlers.iter().map(|h| h.name().to_string()).collect()
}
#[cfg(test)]
mod tests {
use serde::Deserialize;
use super::*;
use crate::context::WorkflowContext;
use crate::handler::HandlerFuture;
#[derive(Serialize, Deserialize, JsonSchema)]
struct ProbeInput {
count: u32,
label: Option<String>,
}
struct Probe;
impl WorkflowHandler for Probe {
fn name(&self) -> &str {
"probe"
}
fn input_schema(&self) -> Option<Value> {
Self::typed_input_schema()
}
fn execute<'a>(&'a self, _ctx: &'a mut WorkflowContext) -> HandlerFuture<'a> {
Box::pin(async move { Ok(()) })
}
}
impl TypedWorkflow for Probe {
type Input = ProbeInput;
}
struct Other;
impl WorkflowHandler for Other {
fn name(&self) -> &str {
"other-été"
}
fn execute<'a>(&'a self, _ctx: &'a mut WorkflowContext) -> HandlerFuture<'a> {
Box::pin(async move { Ok(()) })
}
}
#[test]
fn typed_input_schema_describes_the_input_type() {
let schema = Probe::typed_input_schema().expect("a schema");
assert_eq!(schema, input_schema_for::<ProbeInput>());
assert!(schema["properties"]["count"].is_object());
assert!(schema["properties"]["label"].is_object());
}
#[test]
fn input_schema_can_return_the_typed_schema() {
assert_eq!(Probe.input_schema(), Probe::typed_input_schema());
assert_eq!(Probe.describe().input_schema, Probe::typed_input_schema());
}
#[test]
fn sub_workflow_names_keeps_the_order_of_the_handlers() {
assert_eq!(
sub_workflow_names(&[&Other, &Probe]),
vec!["other-été".to_string(), "probe".to_string()]
);
}
#[test]
fn sub_workflow_names_of_nothing_is_empty() {
assert!(sub_workflow_names(&[]).is_empty());
}
}