telltale_vm/
transfer_semantics.rs1use crate::coroutine::{Coroutine, Fault, Value};
4use crate::faults::{
5 transfer_fault_endpoint_not_owned, transfer_fault_expect_endpoint_register,
6 transfer_fault_expect_nat_target, transfer_fault_target_id_out_of_range,
7};
8use crate::instr::Endpoint;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct TransferRequest {
13 pub endpoint: Endpoint,
15 pub target_id: usize,
17}
18
19pub fn decode_transfer_request(
26 coro: &Coroutine,
27 role: &str,
28 endpoint_reg: u16,
29 target_reg: u16,
30) -> Result<TransferRequest, Fault> {
31 let endpoint_val = coro
32 .regs
33 .get(usize::from(endpoint_reg))
34 .ok_or(Fault::OutOfRegisters)?
35 .clone();
36 let endpoint = match endpoint_val {
37 Value::Endpoint(endpoint) => endpoint,
38 _ => return Err(transfer_fault_expect_endpoint_register(role)),
39 };
40
41 let target_val = coro
42 .regs
43 .get(usize::from(target_reg))
44 .ok_or(Fault::OutOfRegisters)?
45 .clone();
46 let target_id = match target_val {
47 Value::Nat(v) => {
48 usize::try_from(v).map_err(|_| transfer_fault_target_id_out_of_range(role))?
49 }
50 _ => return Err(transfer_fault_expect_nat_target(role)),
51 };
52
53 if !coro.owned_endpoints.contains(&endpoint) {
54 return Err(transfer_fault_endpoint_not_owned());
55 }
56
57 Ok(TransferRequest {
58 endpoint,
59 target_id,
60 })
61}
62
63pub fn move_endpoint_bundle(
71 endpoint: &Endpoint,
72 source: &mut Coroutine,
73 target: Option<&mut Coroutine>,
74) -> Result<(), Fault> {
75 if !source.owned_endpoints.contains(endpoint) {
76 return Err(transfer_fault_endpoint_not_owned());
77 }
78
79 let mut moved_tokens = Vec::new();
80 source.progress_tokens.retain(|token| {
81 if token.endpoint == *endpoint {
82 moved_tokens.push(token.clone());
83 false
84 } else {
85 true
86 }
87 });
88 let mut moved_knowledge = Vec::new();
89 source.knowledge_set.retain(|fact| {
90 if fact.endpoint == *endpoint {
91 moved_knowledge.push(fact.clone());
92 false
93 } else {
94 true
95 }
96 });
97 source.owned_endpoints.retain(|e| e != endpoint);
98
99 if let Some(target) = target {
100 target.owned_endpoints.push(endpoint.clone());
101 target.progress_tokens.extend(moved_tokens);
102 target.knowledge_set.extend(moved_knowledge);
103 } else {
104 source.owned_endpoints.push(endpoint.clone());
105 source.progress_tokens.extend(moved_tokens);
106 source.knowledge_set.extend(moved_knowledge);
107 }
108
109 Ok(())
110}
111
112#[must_use]
114pub fn endpoint_owner_ids(coroutines: &[Coroutine], endpoint: &Endpoint) -> Vec<usize> {
115 coroutines
116 .iter()
117 .filter_map(|coro| coro.owned_endpoints.contains(endpoint).then_some(coro.id))
118 .collect()
119}