meerkat_runtime/
interrupt_public_result.rs1use crate::meerkat_machine::dsl;
2use crate::runtime_state::RuntimeState;
3use crate::traits::RuntimeDriverError;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum UserInterruptObservation {
7 Accepted,
8 StagedNoop,
9 NotReady(RuntimeState),
10 Destroyed,
11 NotInterruptible,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum UserInterruptPublicResult {
16 Interrupted,
17 StagedNoop,
20 NotFound,
21 SessionBusy,
22 Conflict,
23}
24
25impl UserInterruptObservation {
26 fn into_dsl(self) -> dsl::UserInterruptObservationKind {
27 match self {
28 Self::Accepted => dsl::UserInterruptObservationKind::Accepted,
29 Self::StagedNoop => dsl::UserInterruptObservationKind::StagedNoop,
30 Self::NotReady(RuntimeState::Idle) => dsl::UserInterruptObservationKind::IdleNoop,
31 Self::NotReady(RuntimeState::Attached) => {
32 dsl::UserInterruptObservationKind::AttachedNoop
33 }
34 Self::NotReady(RuntimeState::Destroyed) | Self::Destroyed => {
35 dsl::UserInterruptObservationKind::Destroyed
36 }
37 Self::NotReady(_) | Self::NotInterruptible => {
38 dsl::UserInterruptObservationKind::NotInterruptible
39 }
40 }
41 }
42}
43
44pub fn resolve_user_interrupt_public_result(
45 observation: UserInterruptObservation,
46 target_present: bool,
47 staged_promotion_busy: bool,
48) -> Result<UserInterruptPublicResult, RuntimeDriverError> {
49 let mut authority = dsl::MeerkatMachineAuthority::new();
50 let transition = dsl::MeerkatMachineMutator::apply(
51 &mut authority,
52 dsl::MeerkatMachineInput::ResolveUserInterruptPublicResult {
53 observation: observation.into_dsl(),
54 target_present,
55 staged_promotion_busy,
56 },
57 )
58 .map_err(|err| {
59 RuntimeDriverError::Internal(crate::meerkat_machine::dsl_authority::map_error(
60 err,
61 "ResolveUserInterruptPublicResult",
62 ))
63 })?;
64
65 let mut resolved = None;
66 for effect in transition.into_effects() {
67 let dsl::MeerkatMachineEffect::UserInterruptPublicResultResolved { result } = effect else {
68 return Err(RuntimeDriverError::Internal(format!(
69 "unexpected user interrupt public-result effect: {effect:?}"
70 )));
71 };
72 if resolved.replace(result).is_some() {
73 return Err(RuntimeDriverError::Internal(
74 "generated user interrupt authority emitted multiple public results".to_string(),
75 ));
76 }
77 }
78
79 match resolved.ok_or_else(|| {
80 RuntimeDriverError::Internal(
81 "generated user interrupt authority emitted no public result".to_string(),
82 )
83 })? {
84 dsl::UserInterruptPublicResultKind::Interrupted => {
85 Ok(UserInterruptPublicResult::Interrupted)
86 }
87 dsl::UserInterruptPublicResultKind::StagedNoop => Ok(UserInterruptPublicResult::StagedNoop),
88 dsl::UserInterruptPublicResultKind::NotFound => Ok(UserInterruptPublicResult::NotFound),
89 dsl::UserInterruptPublicResultKind::SessionBusy => {
90 Ok(UserInterruptPublicResult::SessionBusy)
91 }
92 dsl::UserInterruptPublicResultKind::Conflict => Ok(UserInterruptPublicResult::Conflict),
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99
100 #[test]
101 fn generated_interrupt_result_classifies_noop_success() {
102 let result = resolve_user_interrupt_public_result(
103 UserInterruptObservation::NotReady(RuntimeState::Idle),
104 true,
105 false,
106 )
107 .expect("idle interrupt result should classify");
108 assert_eq!(result, UserInterruptPublicResult::Interrupted);
109 }
110
111 #[test]
112 fn generated_interrupt_result_classifies_staged_noop_success() {
113 let result =
116 resolve_user_interrupt_public_result(UserInterruptObservation::StagedNoop, true, false)
117 .expect("staged noop interrupt result should classify");
118 assert_eq!(result, UserInterruptPublicResult::StagedNoop);
119 }
120
121 #[test]
122 fn generated_interrupt_result_classifies_destroyed_missing_as_not_found() {
123 let result =
124 resolve_user_interrupt_public_result(UserInterruptObservation::Destroyed, false, false)
125 .expect("destroyed missing interrupt result should classify");
126 assert_eq!(result, UserInterruptPublicResult::NotFound);
127 }
128
129 #[test]
130 fn generated_interrupt_result_classifies_promoting_rejection_as_session_busy() {
131 let result = resolve_user_interrupt_public_result(
132 UserInterruptObservation::NotInterruptible,
133 true,
134 true,
135 )
136 .expect("promoting interrupt result should classify");
137 assert_eq!(result, UserInterruptPublicResult::SessionBusy);
138 }
139}