use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::kernel::state::KernelState;
use crate::kernel::step::Next;
use crate::kernel::KernelError;
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ExecutionStepInput {
Initial,
Resume(Value),
Signal { name: String, value: Value },
}
impl ExecutionStepInput {
pub fn validate(&self) -> Result<(), KernelError> {
match self {
ExecutionStepInput::Initial => Ok(()),
ExecutionStepInput::Resume(_) => Ok(()),
ExecutionStepInput::Signal { name, .. } => {
if name.is_empty() {
return Err(KernelError::Driver(
"ExecutionStepInput::Signal name must be non-empty".into(),
));
}
Ok(())
}
}
}
}
pub type StepResult = Next;
pub trait ExecutionStep<S: KernelState>: Send + Sync {
fn execute(&self, state: &S, input: &ExecutionStepInput) -> Result<StepResult, KernelError>;
}
impl<S, F> ExecutionStep<S> for F
where
S: KernelState,
F: crate::kernel::step::StepFn<S>,
{
fn execute(&self, state: &S, _input: &ExecutionStepInput) -> Result<StepResult, KernelError> {
self.next(state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernel::event::Event;
use crate::kernel::state::KernelState;
use crate::kernel::step::StepFn;
#[derive(Clone, Debug)]
struct TestState(u32);
impl KernelState for TestState {
fn version(&self) -> u32 {
1
}
}
#[test]
fn execution_step_input_initial_validates() {
let input = ExecutionStepInput::Initial;
assert!(input.validate().is_ok());
}
#[test]
fn execution_step_input_resume_validates() {
let input = ExecutionStepInput::Resume(serde_json::json!({"ok": true}));
assert!(input.validate().is_ok());
}
#[test]
fn execution_step_input_signal_empty_name_invalid() {
let input = ExecutionStepInput::Signal {
name: String::new(),
value: serde_json::json!(null),
};
assert!(input.validate().is_err());
}
#[test]
fn execution_step_input_signal_non_empty_name_validates() {
let input = ExecutionStepInput::Signal {
name: "evt".to_string(),
value: serde_json::json!(1),
};
assert!(input.validate().is_ok());
}
struct StepThatEmits;
impl StepFn<TestState> for StepThatEmits {
fn next(&self, state: &TestState) -> Result<Next, KernelError> {
Ok(Next::Emit(vec![Event::StateUpdated {
step_id: None,
payload: serde_json::json!({ "n": state.0 }),
}]))
}
}
#[test]
fn step_fn_impls_execution_step() {
let step = StepThatEmits;
let state = TestState(42);
let input = ExecutionStepInput::Initial;
let result = step.execute(&state, &input).unwrap();
match result {
Next::Emit(evs) => {
assert_eq!(evs.len(), 1);
}
_ => panic!("expected Emit"),
}
}
}