1use std::rc::Rc;
2
3use derive_more::Display;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7use crate::value::{StructureField, Value};
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct Call {
12 #[serde(default)]
15 pub module_id: Option<Uuid>,
16 pub id: Uuid,
18 #[serde(default)]
20 pub args: Vec<StructureField>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
24pub struct CallResult {
25 pub ret: Value,
26 #[serde(default)]
27 pub mutated: Vec<StructureField>,
28}
29
30pub trait Callable {
33 fn call(&self, caller: &mut dyn CallBridge) -> Result<Value, CallError>;
34}
35
36pub trait CallBridge {
40 fn arora_call(&mut self, call: Call) -> Result<CallResult, CallError>;
43
44 fn arora_register_callable(&mut self, callable: Rc<dyn Callable>) -> CallableId;
48
49 fn arora_unregister_callable(&mut self, callable_id: &CallableId);
51
52 fn arora_call_indirect(&mut self, callable_id: &CallableId) -> Result<Value, CallError>;
54}
55
56#[derive(Display, Debug)]
57pub enum CallError {
58 Generic {
59 message: String,
60 },
61 ModuleNotFound {
62 id: Uuid,
63 },
64 FunctionNotFound {
65 id: Uuid,
66 },
67 Trap {
68 message: String,
69 },
70 Internal {
71 message: String,
72 },
73 Guest {
75 message: String,
76 },
77}
78
79impl std::error::Error for CallError {}
80
81#[derive(Clone, Hash, Eq, PartialEq)]
82pub struct CallableId {
83 pub id: u64,
84}
85
86impl From<u64> for CallableId {
87 fn from(id: u64) -> Self {
88 Self { id }
89 }
90}
91
92impl Callable for CallableId {
93 fn call(&self, caller: &mut dyn CallBridge) -> Result<Value, CallError> {
94 caller.arora_call_indirect(self)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use crate::value::{Structure, Value};
101
102 use super::*;
103 use std::str::FromStr;
104 use uuid::Uuid;
105
106 #[test]
107 pub fn parse_call_test() {
108 let call: Call = serde_yaml::with::singleton_map_recursive::deserialize(
110 serde_yaml::Deserializer::from_str(CALL_TEST),
111 )
112 .unwrap();
113 assert_eq!(
114 call.id,
115 Uuid::from_str("07f5740c-ba4a-45af-8ec5-bedde5737e99").unwrap()
116 );
117 if let Value::Structure(Structure { id, fields }) = &call.args[1].value.as_ref() {
118 assert_eq!(
119 *id,
120 Uuid::from_str("7f9aedf8-dbde-4020-b5f4-c28a6635ae7c").unwrap()
121 );
122 if let Value::I32(v) = fields[1].value.as_ref() {
123 assert_eq!(*v, 113);
124 } else {
125 panic!("expected i32 value under second field of struct arg");
126 }
127 } else {
128 panic!("expected a string under arg 55dbec70-1c3a-433e-a6e6-27446b7f065e");
129 }
130 }
131
132 #[test]
133 pub fn parse_call_test_2() {
134 let call: Call = serde_yaml::with::singleton_map_recursive::deserialize(
135 serde_yaml::Deserializer::from_str(CALL_TEST_2),
136 )
137 .unwrap();
138 assert_eq!(
139 call.id,
140 Uuid::from_str("b213a552-77ad-465a-a26d-352e8eccfd63").unwrap()
141 );
142 assert_eq!(call.args.len(), 2);
143 }
144
145 #[test]
148 fn callable_round_trips_through_a_mock_bridge() {
149 use std::collections::HashMap;
150 use std::rc::Rc;
151
152 #[derive(Default)]
153 struct MockBridge {
154 registered: HashMap<CallableId, Rc<dyn Callable>>,
155 next_id: u64,
156 }
157
158 impl CallBridge for MockBridge {
159 fn arora_call(&mut self, _call: Call) -> Result<CallResult, CallError> {
160 Err(CallError::Generic {
161 message: "the mock has no modules".to_string(),
162 })
163 }
164 fn arora_register_callable(&mut self, callable: Rc<dyn Callable>) -> CallableId {
165 let id = CallableId::from(self.next_id);
166 self.next_id += 1;
167 self.registered.insert(id.clone(), callable);
168 id
169 }
170 fn arora_unregister_callable(&mut self, callable_id: &CallableId) {
171 self.registered.remove(callable_id);
172 }
173 fn arora_call_indirect(&mut self, callable_id: &CallableId) -> Result<Value, CallError> {
174 let callable = self
175 .registered
176 .get(callable_id)
177 .cloned()
178 .ok_or(CallError::Generic {
179 message: "unknown callable".to_string(),
180 })?;
181 callable.call(self)
182 }
183 }
184
185 struct Answer;
186 impl Callable for Answer {
187 fn call(&self, _caller: &mut dyn CallBridge) -> Result<Value, CallError> {
188 Ok(Value::I32(42))
189 }
190 }
191
192 let mut bridge = MockBridge::default();
193 let id = bridge.arora_register_callable(Rc::new(Answer));
194 let result = bridge.arora_call_indirect(&id).unwrap();
195 assert!(matches!(result, Value::I32(42)));
196
197 bridge.arora_unregister_callable(&id);
198 assert!(bridge.arora_call_indirect(&id).is_err());
199 }
200
201 pub const CALL_TEST: &str = "\
202id: 07f5740c-ba4a-45af-8ec5-bedde5737e99
203args:
204- id: b41899c3-66dc-40d4-ab61-d1ccf5231c88
205 value:
206 enum:
207 id: 325a5767-e344-4532-860e-0749bcf2e428
208 variant_id: 766e9e9a-446d-4e46-83e6-14b7ca101169
209 value: unit
210- id: 63086e48-804f-403a-8862-3358ddedc08d
211 value:
212 struct:
213 id: 7f9aedf8-dbde-4020-b5f4-c28a6635ae7c
214 fields:
215 - id: 7d94a956-e50d-4cc4-9714-f62e1f9b134e
216 value:
217 enums:
218 id: 325a5767-e344-4532-860e-0749bcf2e428
219 elements:
220 - variant_id: 2468f46c-bb60-425c-9a4d-9ad326ccc7e2
221 value: unit
222 - id: 5ffa9104-1e5c-4026-943f-8db38bd34563
223 value:
224 i32: 113
225";
226
227 pub const CALL_TEST_2: &str = "\
228id: b213a552-77ad-465a-a26d-352e8eccfd63
229args:
230- id: 55dbec70-1c3a-433e-a6e6-27446b7f065e
231 value:
232 u32: 42
233- id: abf9ca4e-e03f-431a-a32b-4911f809c399
234 value:
235 u32: 64
236";
237}