telltale_machine/
faults.rs1use crate::coroutine::Fault;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8pub enum FaultClass {
9 Type,
11 Label,
13 Channel,
15 Verification,
17 Invoke,
19 Acquire,
21 Transfer,
23 Speculation,
25 Close,
27 Flow,
29 Progress,
31 OutputCondition,
33 Register,
35 ProgramCounter,
37 Buffer,
39 Credits,
41}
42
43#[must_use]
45pub fn classify_fault(fault: &Fault) -> FaultClass {
46 match fault {
47 Fault::TypeViolation { .. } => FaultClass::Type,
48 Fault::UnknownLabel { .. } => FaultClass::Label,
49 Fault::ChannelClosed { .. } => FaultClass::Channel,
50 Fault::InvalidSignature { .. } | Fault::VerificationFailed { .. } => {
51 FaultClass::Verification
52 }
53 Fault::Invoke { .. } => FaultClass::Invoke,
54 Fault::Acquire { .. } => FaultClass::Acquire,
55 Fault::Transfer { .. } => FaultClass::Transfer,
56 Fault::Speculation { .. } => FaultClass::Speculation,
57 Fault::Close { .. } => FaultClass::Close,
58 Fault::FlowViolation { .. } => FaultClass::Flow,
59 Fault::NoProgressToken { .. } => FaultClass::Progress,
60 Fault::OutputCondition { .. } => FaultClass::OutputCondition,
61 Fault::OutOfRegisters => FaultClass::Register,
62 Fault::PcOutOfBounds => FaultClass::ProgramCounter,
63 Fault::BufferFull { .. } => FaultClass::Buffer,
64 Fault::OutOfCredits => FaultClass::Credits,
65 }
66}
67
68#[must_use]
70pub fn fault_code(class: FaultClass) -> &'static str {
71 match class {
72 FaultClass::Type => "machine.fault.type",
73 FaultClass::Label => "machine.fault.label",
74 FaultClass::Channel => "machine.fault.channel",
75 FaultClass::Verification => "machine.fault.verification",
76 FaultClass::Invoke => "machine.fault.invoke",
77 FaultClass::Acquire => "machine.fault.acquire",
78 FaultClass::Transfer => "machine.fault.transfer",
79 FaultClass::Speculation => "machine.fault.speculation",
80 FaultClass::Close => "machine.fault.close",
81 FaultClass::Flow => "machine.fault.flow",
82 FaultClass::Progress => "machine.fault.progress",
83 FaultClass::OutputCondition => "machine.fault.output_condition",
84 FaultClass::Register => "machine.fault.register",
85 FaultClass::ProgramCounter => "machine.fault.pc",
86 FaultClass::Buffer => "machine.fault.buffer",
87 FaultClass::Credits => "machine.fault.credits",
88 }
89}
90
91#[must_use]
93pub fn fault_code_of(fault: &Fault) -> &'static str {
94 fault_code(classify_fault(fault))
95}
96
97#[must_use]
99pub fn transfer_fault_expect_endpoint_register(role: &str) -> Fault {
100 Fault::Transfer {
101 message: format!("{role}: transfer expects endpoint register"),
102 }
103}
104
105#[must_use]
107pub fn transfer_fault_expect_nat_target(role: &str) -> Fault {
108 Fault::Transfer {
109 message: format!("{role}: transfer expects nat target coroutine id"),
110 }
111}
112
113#[must_use]
115pub fn transfer_fault_target_id_out_of_range(role: &str) -> Fault {
116 Fault::Transfer {
117 message: format!("{role}: target id out of range"),
118 }
119}
120
121#[must_use]
123pub fn transfer_fault_endpoint_not_owned() -> Fault {
124 Fault::Transfer {
125 message: "endpoint not owned".to_string(),
126 }
127}
128
129#[must_use]
131pub fn transfer_fault_delegation_guard_violation(phase: &str) -> Fault {
132 Fault::Transfer {
133 message: format!("delegation guard violation {phase} transfer"),
134 }
135}
136
137#[must_use]
139pub fn speculation_fault_disabled() -> Fault {
140 Fault::Speculation {
141 message: "speculation disabled".to_string(),
142 }
143}
144
145#[must_use]
147pub fn speculation_fault_join_requires_active() -> Fault {
148 Fault::Speculation {
149 message: "join requires active speculation".to_string(),
150 }
151}
152
153#[must_use]
155pub fn speculation_fault_abort_requires_active() -> Fault {
156 Fault::Speculation {
157 message: "abort requires active speculation".to_string(),
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164 use crate::instr::Endpoint;
165 use crate::session::Edge;
166 use telltale_types::ValType;
167
168 #[test]
169 fn fault_codes_are_stable_for_representative_variants() {
170 let samples = [
171 Fault::TypeViolation {
172 expected: ValType::Unit,
173 actual: ValType::Nat,
174 message: "m".to_string(),
175 },
176 Fault::UnknownLabel {
177 label: "x".to_string(),
178 },
179 Fault::ChannelClosed {
180 endpoint: Endpoint {
181 sid: 1,
182 role: "A".to_string(),
183 },
184 },
185 Fault::VerificationFailed {
186 edge: Edge::new(1, "A", "B"),
187 message: "bad sig".to_string(),
188 },
189 Fault::OutOfCredits,
190 ];
191
192 let codes: Vec<&str> = samples.iter().map(fault_code_of).collect();
193 assert_eq!(
194 codes,
195 vec![
196 "machine.fault.type",
197 "machine.fault.label",
198 "machine.fault.channel",
199 "machine.fault.verification",
200 "machine.fault.credits",
201 ]
202 );
203 }
204}