Set up Go module and move Rust to legacy
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,327 @@
|
||||
use crate::domain::{CommitmentState, RuntimeState};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RuntimeAction {
|
||||
EnterPlanning,
|
||||
CancelOrTimeout,
|
||||
Activate { policy_accepted: bool },
|
||||
StartTransition,
|
||||
CompleteForReview,
|
||||
SevereViolation,
|
||||
ReturnToActive,
|
||||
SwitchTask,
|
||||
EndTransitionForReview,
|
||||
ContinuePlanning,
|
||||
EndWorkPeriod,
|
||||
EnterAdminOverride,
|
||||
ExitAdminOverride(RuntimeState),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum CommitmentAction {
|
||||
Activate,
|
||||
PauseForTransition,
|
||||
ReturnFromPause,
|
||||
Complete,
|
||||
Abandon,
|
||||
Violate,
|
||||
RecoverExplicitly,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum TransitionError {
|
||||
PolicyRejected,
|
||||
IllegalRuntimeTransition {
|
||||
current: RuntimeState,
|
||||
action: RuntimeAction,
|
||||
},
|
||||
IllegalCommitmentTransition {
|
||||
current: CommitmentState,
|
||||
action: CommitmentAction,
|
||||
},
|
||||
InvalidAdminOverrideExitTarget {
|
||||
target: RuntimeState,
|
||||
},
|
||||
}
|
||||
|
||||
impl std::fmt::Display for TransitionError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
TransitionError::PolicyRejected => write!(f, "policy was rejected"),
|
||||
TransitionError::IllegalRuntimeTransition { current, action } => {
|
||||
write!(
|
||||
f,
|
||||
"illegal runtime transition from {:?} via {:?}",
|
||||
current, action
|
||||
)
|
||||
}
|
||||
TransitionError::IllegalCommitmentTransition { current, action } => {
|
||||
write!(
|
||||
f,
|
||||
"illegal commitment transition from {:?} via {:?}",
|
||||
current, action
|
||||
)
|
||||
}
|
||||
TransitionError::InvalidAdminOverrideExitTarget { target } => {
|
||||
write!(f, "invalid admin override exit target {:?}", target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for TransitionError {}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CompositeTransition {
|
||||
pub runtime_state: RuntimeState,
|
||||
pub commitment_state: CommitmentState,
|
||||
}
|
||||
|
||||
pub fn transition_runtime(
|
||||
current: RuntimeState,
|
||||
action: RuntimeAction,
|
||||
) -> Result<RuntimeState, TransitionError> {
|
||||
use RuntimeAction::*;
|
||||
use RuntimeState::*;
|
||||
|
||||
match (current, action) {
|
||||
(Locked, EnterPlanning) => Ok(Planning),
|
||||
(
|
||||
Planning,
|
||||
Activate {
|
||||
policy_accepted: true,
|
||||
},
|
||||
) => Ok(Active),
|
||||
(
|
||||
Planning,
|
||||
Activate {
|
||||
policy_accepted: false,
|
||||
},
|
||||
) => Err(TransitionError::PolicyRejected),
|
||||
(Planning, CancelOrTimeout) => Ok(Locked),
|
||||
(Active, StartTransition) => Ok(Transition),
|
||||
(Active, CompleteForReview) => Ok(Review),
|
||||
(Active, SevereViolation) => Ok(Locked),
|
||||
(Transition, ReturnToActive) => Ok(Active),
|
||||
(Transition, SwitchTask) => Ok(Planning),
|
||||
(Transition, EndTransitionForReview) => Ok(Review),
|
||||
(Transition, CancelOrTimeout) => Ok(Locked),
|
||||
(Review, ContinuePlanning) => Ok(Planning),
|
||||
(Review, EndWorkPeriod) => Ok(Locked),
|
||||
(_, EnterAdminOverride) => Ok(AdminOverride),
|
||||
(AdminOverride, ExitAdminOverride(Locked)) => Ok(Locked),
|
||||
(AdminOverride, ExitAdminOverride(Planning)) => Ok(Planning),
|
||||
(AdminOverride, ExitAdminOverride(target)) => {
|
||||
Err(TransitionError::InvalidAdminOverrideExitTarget { target })
|
||||
}
|
||||
_ => Err(TransitionError::IllegalRuntimeTransition { current, action }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn transition_commitment(
|
||||
current: CommitmentState,
|
||||
action: CommitmentAction,
|
||||
) -> Result<CommitmentState, TransitionError> {
|
||||
use CommitmentAction::*;
|
||||
use CommitmentState::*;
|
||||
|
||||
match (current.clone(), action) {
|
||||
(Draft, Activate) => Ok(Active),
|
||||
(Active, PauseForTransition) => Ok(Paused),
|
||||
(Paused, ReturnFromPause) => Ok(Active),
|
||||
(Active, Complete) => Ok(Completed),
|
||||
(Active, Abandon) => Ok(Abandoned),
|
||||
(Active, Violate) => Ok(Violated),
|
||||
(Paused, Abandon) => Ok(Abandoned),
|
||||
(Violated, RecoverExplicitly) => Ok(Active),
|
||||
(Violated, Abandon) => Ok(Abandoned),
|
||||
_ => Err(TransitionError::IllegalCommitmentTransition { current, action }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_transition(
|
||||
runtime_state: RuntimeState,
|
||||
commitment_state: CommitmentState,
|
||||
) -> Result<CompositeTransition, TransitionError> {
|
||||
let next_runtime = transition_runtime(runtime_state, RuntimeAction::StartTransition)?;
|
||||
let next_commitment =
|
||||
transition_commitment(commitment_state, CommitmentAction::PauseForTransition)?;
|
||||
|
||||
Ok(CompositeTransition {
|
||||
runtime_state: next_runtime,
|
||||
commitment_state: next_commitment,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn planning_can_only_activate_with_valid_policy() {
|
||||
assert_eq!(
|
||||
transition_runtime(
|
||||
RuntimeState::Planning,
|
||||
RuntimeAction::Activate {
|
||||
policy_accepted: true,
|
||||
}
|
||||
),
|
||||
Ok(RuntimeState::Active)
|
||||
);
|
||||
assert_eq!(
|
||||
transition_runtime(
|
||||
RuntimeState::Planning,
|
||||
RuntimeAction::Activate {
|
||||
policy_accepted: false,
|
||||
}
|
||||
),
|
||||
Err(TransitionError::PolicyRejected)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn active_can_enter_transition_or_review_or_locked() {
|
||||
assert_eq!(
|
||||
transition_runtime(RuntimeState::Active, RuntimeAction::StartTransition),
|
||||
Ok(RuntimeState::Transition)
|
||||
);
|
||||
assert_eq!(
|
||||
transition_runtime(RuntimeState::Active, RuntimeAction::CompleteForReview),
|
||||
Ok(RuntimeState::Review)
|
||||
);
|
||||
assert_eq!(
|
||||
transition_runtime(RuntimeState::Active, RuntimeAction::SevereViolation),
|
||||
Ok(RuntimeState::Locked)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_cannot_directly_become_active() {
|
||||
let err = transition_runtime(
|
||||
RuntimeState::Locked,
|
||||
RuntimeAction::Activate {
|
||||
policy_accepted: true,
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
TransitionError::IllegalRuntimeTransition {
|
||||
current: RuntimeState::Locked,
|
||||
action: RuntimeAction::Activate {
|
||||
policy_accepted: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"illegal runtime transition from Locked via Activate { policy_accepted: true }"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_override_cannot_exit_to_admin_override() {
|
||||
let err = transition_runtime(
|
||||
RuntimeState::AdminOverride,
|
||||
RuntimeAction::ExitAdminOverride(RuntimeState::AdminOverride),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
TransitionError::InvalidAdminOverrideExitTarget {
|
||||
target: RuntimeState::AdminOverride,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_override_cannot_exit_to_active_or_transition() {
|
||||
for target in [RuntimeState::Active, RuntimeState::Transition] {
|
||||
let err = transition_runtime(
|
||||
RuntimeState::AdminOverride,
|
||||
RuntimeAction::ExitAdminOverride(target),
|
||||
)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
TransitionError::InvalidAdminOverrideExitTarget { target }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_override_can_exit_to_locked_or_planning() {
|
||||
assert_eq!(
|
||||
transition_runtime(
|
||||
RuntimeState::AdminOverride,
|
||||
RuntimeAction::ExitAdminOverride(RuntimeState::Locked),
|
||||
),
|
||||
Ok(RuntimeState::Locked)
|
||||
);
|
||||
assert_eq!(
|
||||
transition_runtime(
|
||||
RuntimeState::AdminOverride,
|
||||
RuntimeAction::ExitAdminOverride(RuntimeState::Planning),
|
||||
),
|
||||
Ok(RuntimeState::Planning)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commitment_violation_recovery_is_explicit() {
|
||||
assert_eq!(
|
||||
transition_commitment(
|
||||
CommitmentState::Violated,
|
||||
CommitmentAction::RecoverExplicitly
|
||||
),
|
||||
Ok(CommitmentState::Active)
|
||||
);
|
||||
let err =
|
||||
transition_commitment(CommitmentState::Violated, CommitmentAction::ReturnFromPause)
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
TransitionError::IllegalCommitmentTransition {
|
||||
current: CommitmentState::Violated,
|
||||
action: CommitmentAction::ReturnFromPause,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"illegal commitment transition from Violated via ReturnFromPause"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_transition_moves_runtime_and_commitment_together() {
|
||||
assert_eq!(
|
||||
start_transition(RuntimeState::Active, CommitmentState::Active),
|
||||
Ok(CompositeTransition {
|
||||
runtime_state: RuntimeState::Transition,
|
||||
commitment_state: CommitmentState::Paused,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start_transition_fails_before_returning_partial_state() {
|
||||
assert_eq!(
|
||||
start_transition(RuntimeState::Active, CommitmentState::Draft),
|
||||
Err(TransitionError::IllegalCommitmentTransition {
|
||||
current: CommitmentState::Draft,
|
||||
action: CommitmentAction::PauseForTransition,
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
start_transition(RuntimeState::Locked, CommitmentState::Active),
|
||||
Err(TransitionError::IllegalRuntimeTransition {
|
||||
current: RuntimeState::Locked,
|
||||
action: RuntimeAction::StartTransition,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user