1use crate::delegation::{self, DelegateKind, DelegationVerdict, Depth};
15use crate::error::{AxCode, AxError, GateRefusal};
16
17use super::GateOutcome;
18
19pub fn spawn(parent: Depth, kind: &DelegateKind) -> GateOutcome {
22 match delegation::admit(parent, kind) {
23 DelegationVerdict::Allow => GateOutcome::Allow,
24 DelegationVerdict::Deny => GateOutcome::Deny {
25 refusal: Box::new(
26 AxError::refusal(
27 AxCode::DelegationDepth,
28 "spawn delegate",
29 format!("{kind:?}"),
30 GateRefusal::new(
31 "delegates do not delegate: one level deep",
32 "a delegated position requested a spawn",
33 "return this subtask to the resident who spawned you; \
34 that resident can delegate it",
35 ),
36 )
37 .with_recovery(
38 "finish this subtask yourself, or end the turn with what you have so \
39 the resident who spawned you can delegate the rest",
40 ),
41 ),
42 },
43 }
44}
45
46#[cfg(test)]
47#[allow(
48 clippy::unwrap_used,
49 clippy::expect_used,
50 clippy::panic,
51 reason = "test code"
52)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn the_spawn_door_teaches_the_way_back_up() {
58 assert!(matches!(
59 spawn(Depth::Root, &DelegateKind::Ephemeral),
60 GateOutcome::Allow
61 ));
62 let GateOutcome::Deny { refusal } = spawn(Depth::Delegated, &DelegateKind::Ephemeral)
63 else {
64 panic!("a delegate may not delegate")
65 };
66 assert_eq!(refusal.code(), &AxCode::DelegationDepth);
67 let parts = refusal.gate().expect("gate refusals carry three parts");
68 assert!(!parts.rule().is_empty());
69 assert!(!parts.violation().is_empty());
70 assert!(parts.alternative().contains("spawned you"));
71 }
72}