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)]
11#[cfg_attr(feature = "derive", derive(crate::AroraType))]
12#[cfg_attr(feature = "derive", arora(id = "9fe95b4c-08f9-48cf-8c10-99686e7b2afd"))]
13pub struct Call {
14 #[cfg_attr(feature = "derive", arora(id = "8c4633be-578a-405e-9c58-75c3bbf194be"))]
17 #[serde(default)]
18 pub module_id: Option<Uuid>,
19 #[cfg_attr(feature = "derive", arora(id = "1bd7f3de-6413-45b2-8813-8a2f415dd29d"))]
21 pub id: Uuid,
22 #[cfg_attr(
26 feature = "derive",
27 arora(id = "7852eaaf-23e9-4762-a206-cc4b9c9984a7", keyvalue)
28 )]
29 #[serde(default)]
30 pub args: Vec<StructureField>,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34pub struct CallResult {
35 pub ret: Value,
36 #[serde(default)]
37 pub mutated: Vec<StructureField>,
38}
39
40pub trait Callable {
43 fn call(&self, caller: &mut dyn CallBridge) -> Result<Value, CallError>;
44}
45
46pub trait CallBridge {
50 fn arora_call(&mut self, call: Call) -> Result<CallResult, CallError>;
53
54 fn arora_register_callable(&mut self, callable: Rc<dyn Callable>) -> CallableId;
58
59 fn arora_unregister_callable(&mut self, callable_id: &CallableId);
61
62 fn arora_call_indirect(&mut self, callable_id: &CallableId) -> Result<Value, CallError>;
64}
65
66#[derive(Display, Debug)]
67pub enum CallError {
68 Generic {
69 message: String,
70 },
71 ModuleNotFound {
72 id: Uuid,
73 },
74 FunctionNotFound {
75 id: Uuid,
76 },
77 Trap {
78 message: String,
79 },
80 Internal {
81 message: String,
82 },
83 Guest {
85 message: String,
86 },
87}
88
89impl std::error::Error for CallError {}
90
91#[derive(Clone, Hash, Eq, PartialEq)]
92pub struct CallableId {
93 pub id: u64,
94}
95
96impl From<u64> for CallableId {
97 fn from(id: u64) -> Self {
98 Self { id }
99 }
100}
101
102impl Callable for CallableId {
103 fn call(&self, caller: &mut dyn CallBridge) -> Result<Value, CallError> {
104 caller.arora_call_indirect(self)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use crate::value::{Structure, Value};
111
112 use super::*;
113 use std::str::FromStr;
114 use uuid::Uuid;
115
116 #[test]
117 pub fn parse_call_test() {
118 let call: Call = serde_yaml::with::singleton_map_recursive::deserialize(
120 serde_yaml::Deserializer::from_str(CALL_TEST),
121 )
122 .unwrap();
123 assert_eq!(
124 call.id,
125 Uuid::from_str("07f5740c-ba4a-45af-8ec5-bedde5737e99").unwrap()
126 );
127 if let Value::Structure(Structure { id, fields }) = &call.args[1].value.as_ref() {
128 assert_eq!(
129 *id,
130 Uuid::from_str("7f9aedf8-dbde-4020-b5f4-c28a6635ae7c").unwrap()
131 );
132 if let Value::I32(v) = fields[1].value.as_ref() {
133 assert_eq!(*v, 113);
134 } else {
135 panic!("expected i32 value under second field of struct arg");
136 }
137 } else {
138 panic!("expected a string under arg 55dbec70-1c3a-433e-a6e6-27446b7f065e");
139 }
140 }
141
142 #[test]
143 pub fn parse_call_test_2() {
144 let call: Call = serde_yaml::with::singleton_map_recursive::deserialize(
145 serde_yaml::Deserializer::from_str(CALL_TEST_2),
146 )
147 .unwrap();
148 assert_eq!(
149 call.id,
150 Uuid::from_str("b213a552-77ad-465a-a26d-352e8eccfd63").unwrap()
151 );
152 assert_eq!(call.args.len(), 2);
153 }
154
155 #[cfg(feature = "derive")]
159 #[test]
160 fn call_arora_type_shapes_its_fields() {
161 use crate::module::low::TypeRef;
162 use crate::ty::low::TypeKind;
163 use crate::AroraType;
164
165 let ty = Call::arora_type();
166 let TypeKind::Structure(structure) = &ty.kind else {
167 panic!("Call is a structure type");
168 };
169 let field = |id: &str| {
170 structure.fields[&Uuid::from_str(id).unwrap()]
171 .type_ref
172 .clone()
173 };
174 assert!(matches!(
176 field("8c4633be-578a-405e-9c58-75c3bbf194be"),
177 TypeRef::Option { id } if id == *crate::ty::UUID_ID
178 ));
179 assert!(matches!(
181 field("1bd7f3de-6413-45b2-8813-8a2f415dd29d"),
182 TypeRef::Scalar { id } if id == *crate::ty::UUID_ID
183 ));
184 assert!(matches!(
186 field("7852eaaf-23e9-4762-a206-cc4b9c9984a7"),
187 TypeRef::Scalar { id } if id == *crate::ty::KEY_VALUE_ID
188 ));
189 }
190
191 #[test]
194 fn callable_round_trips_through_a_mock_bridge() {
195 use std::collections::HashMap;
196 use std::rc::Rc;
197
198 #[derive(Default)]
199 struct MockBridge {
200 registered: HashMap<CallableId, Rc<dyn Callable>>,
201 next_id: u64,
202 }
203
204 impl CallBridge for MockBridge {
205 fn arora_call(&mut self, _call: Call) -> Result<CallResult, CallError> {
206 Err(CallError::Generic {
207 message: "the mock has no modules".to_string(),
208 })
209 }
210 fn arora_register_callable(&mut self, callable: Rc<dyn Callable>) -> CallableId {
211 let id = CallableId::from(self.next_id);
212 self.next_id += 1;
213 self.registered.insert(id.clone(), callable);
214 id
215 }
216 fn arora_unregister_callable(&mut self, callable_id: &CallableId) {
217 self.registered.remove(callable_id);
218 }
219 fn arora_call_indirect(&mut self, callable_id: &CallableId) -> Result<Value, CallError> {
220 let callable = self
221 .registered
222 .get(callable_id)
223 .cloned()
224 .ok_or(CallError::Generic {
225 message: "unknown callable".to_string(),
226 })?;
227 callable.call(self)
228 }
229 }
230
231 struct Answer;
232 impl Callable for Answer {
233 fn call(&self, _caller: &mut dyn CallBridge) -> Result<Value, CallError> {
234 Ok(Value::I32(42))
235 }
236 }
237
238 let mut bridge = MockBridge::default();
239 let id = bridge.arora_register_callable(Rc::new(Answer));
240 let result = bridge.arora_call_indirect(&id).unwrap();
241 assert!(matches!(result, Value::I32(42)));
242
243 bridge.arora_unregister_callable(&id);
244 assert!(bridge.arora_call_indirect(&id).is_err());
245 }
246
247 pub const CALL_TEST: &str = "\
248id: 07f5740c-ba4a-45af-8ec5-bedde5737e99
249args:
250- id: b41899c3-66dc-40d4-ab61-d1ccf5231c88
251 value:
252 enum:
253 id: 325a5767-e344-4532-860e-0749bcf2e428
254 variant_id: 766e9e9a-446d-4e46-83e6-14b7ca101169
255 value: unit
256- id: 63086e48-804f-403a-8862-3358ddedc08d
257 value:
258 struct:
259 id: 7f9aedf8-dbde-4020-b5f4-c28a6635ae7c
260 fields:
261 - id: 7d94a956-e50d-4cc4-9714-f62e1f9b134e
262 value:
263 enums:
264 id: 325a5767-e344-4532-860e-0749bcf2e428
265 elements:
266 - variant_id: 2468f46c-bb60-425c-9a4d-9ad326ccc7e2
267 value: unit
268 - id: 5ffa9104-1e5c-4026-943f-8db38bd34563
269 value:
270 i32: 113
271";
272
273 pub const CALL_TEST_2: &str = "\
274id: b213a552-77ad-465a-a26d-352e8eccfd63
275args:
276- id: 55dbec70-1c3a-433e-a6e6-27446b7f065e
277 value:
278 u32: 42
279- id: abf9ca4e-e03f-431a-a32b-4911f809c399
280 value:
281 u32: 64
282";
283}