Skip to main content

dpm_engine/run/
transition.rs

1//! Who may report on a run, and which lifecycle moves are legal.
2
3use super::RunError;
4use dpm_model::{ActorId, ActorKind, RunId, RunRecord, RunState};
5
6/// Refuse an agent that is not the run's executor; humans and services may record any run.
7///
8/// Runs observe an executor, so an agent must not be able to complete or fail another agent's run,
9/// while a service host or an operator can speak for a run it watches or recovers.
10pub fn authorize(
11    record: &RunRecord,
12    writer: &ActorId,
13    action: &'static str,
14) -> Result<(), RunError> {
15    authorize_writer(writer, &record.executor, record.id, action)
16}
17
18pub(super) fn authorize_writer(
19    writer: &ActorId,
20    executor: &ActorId,
21    run: RunId,
22    action: &'static str,
23) -> Result<(), RunError> {
24    if writer == executor || writer.kind != ActorKind::Agent {
25        return Ok(());
26    }
27    Err(RunError::ActorNotAllowed {
28        actor: writer.clone(),
29        action,
30        run,
31        executor: executor.clone(),
32    })
33}
34
35/// Check a move from the run's current state.
36///
37/// Working and waiting alternate, and either may end in any terminal state. A terminal state is
38/// final, and repeating the current state is not a transition.
39pub fn check_transition(run: RunId, from: RunState, to: RunState) -> Result<(), RunError> {
40    if from.is_terminal() {
41        return Err(RunError::Terminal { run, state: from });
42    }
43    if from == to {
44        return Err(RunError::InvalidTransition { run, from, to });
45    }
46    Ok(())
47}