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