1use super::{
2 Callback, ProviderCallbackCodec, ProviderCallbackContext, ProviderExternalCodec,
3 ProviderExternalItem, ProviderStoredInput, ProviderStoredOutput, ProviderStoredOwner,
4 ProviderValueContext, Stored, Value,
5};
6use crate::provider::advanced::{ProviderDynamicInput, ProviderDynamicValue, StoredDynamic};
7use crate::{
8 HostCall, HostCallError, HostListType, HostProfile, HostProvider, HostStoredType, HostType,
9};
10use ecow::EcoString;
11use std::marker::PhantomData;
12
13pub struct Call<State, Context = ProviderCallPlaceholder> {
19 context: Context,
20 state: PhantomData<fn() -> State>,
21}
22
23pub type HostResult<Value> = Result<Value, HostCallError>;
25
26#[doc(hidden)]
27pub struct ProviderCallPlaceholder;
28
29#[doc(hidden)]
30pub struct ProviderSharedCall<'state, State> {
31 state: &'state State,
32}
33
34#[doc(hidden)]
35pub struct ProviderActiveCall<'call, Profile, Provider, Return>
36where
37 Profile: HostProfile,
38 Provider: HostProvider<Profile>,
39 Return: HostType,
40{
41 call: HostCall<'call, Profile, Provider, Return>,
42}
43
44impl<'state, State> Call<State, ProviderSharedCall<'state, State>> {
45 pub fn state(&self) -> &State {
46 self.context.state
47 }
48
49 #[doc(hidden)]
50 pub fn from_shared_state(state: &'state State) -> Self {
51 Self {
52 context: ProviderSharedCall { state },
53 state: PhantomData,
54 }
55 }
56}
57
58impl<'call, Profile, Provider, Return>
59 Call<Provider::State, ProviderActiveCall<'call, Profile, Provider, Return>>
60where
61 Profile: HostProfile,
62 Provider: HostProvider<Profile>,
63 Return: HostType,
64{
65 pub fn state(&mut self) -> &Provider::State {
66 &*self.context.call.state()
67 }
68
69 pub fn state_mut(&mut self) -> &mut Provider::State {
70 self.context.call.state()
71 }
72
73 pub fn equal<Type, Host>(
75 &self,
76 left: &Value<Type, ProviderValueContext<'call, Host>>,
77 right: &Value<Type, ProviderValueContext<'call, Host>>,
78 ) -> bool
79 where
80 Host: HostType,
81 Host::Value<'call>: Clone,
82 {
83 self.context.call.equal::<Host>(left.host(), right.host())
84 }
85
86 pub fn source_hash<Type, Host>(
91 &self,
92 value: &Value<Type, ProviderValueContext<'call, Host>>,
93 ) -> u64
94 where
95 Host: HostType,
96 Host::Value<'call>: Clone,
97 {
98 self.context.call.source_hash::<Host>(value.host())
99 }
100
101 pub fn inspect<Type, Host>(
103 &self,
104 value: &Value<Type, ProviderValueContext<'call, Host>>,
105 ) -> EcoString
106 where
107 Host: HostType,
108 Host::Value<'call>: Clone,
109 {
110 self.context.call.inspect::<Host>(value.host())
111 }
112
113 pub fn list_len<ListType, ItemHost>(
115 &self,
116 value: &Value<ListType, ProviderValueContext<'call, HostListType<ItemHost>>>,
117 ) -> usize
118 where
119 ItemHost: HostType,
120 {
121 self.context.call.list_len(value.host())
122 }
123
124 pub fn list_get<ListType, Item, ItemHost>(
126 &mut self,
127 value: &Value<ListType, ProviderValueContext<'call, HostListType<ItemHost>>>,
128 index: usize,
129 ) -> Option<Value<Item, ProviderValueContext<'call, ItemHost>>>
130 where
131 ItemHost: HostType,
132 {
133 self.context
134 .call
135 .list_item(value.host(), index)
136 .map(Value::from_host)
137 }
138
139 pub fn store<Type, Host, Owner, Index>(
142 &mut self,
143 value: Value<Type, ProviderValueContext<'call, Host>>,
144 ) -> Stored<Type, ProviderStoredOutput<'call, Owner, Index, Host>>
145 where
146 Host: HostType,
147 Owner: ProviderStoredOwner,
148 {
149 Stored::from_output(
150 self.context
151 .call
152 .provider_store::<HostStoredType<Index>, Host>(value.into_host()),
153 )
154 }
155
156 pub fn restore<Type, Host, Owner, Index>(
158 &mut self,
159 value: Stored<Type, ProviderStoredInput<'_, Owner, Index, Host>>,
160 ) -> Value<Type, ProviderValueContext<'call, Host>>
161 where
162 Host: HostType,
163 Owner: ProviderStoredOwner,
164 {
165 Value::from_host(
166 self.context
167 .call
168 .provider_restore::<Host, HostStoredType<Index>>(value.host()),
169 )
170 }
171
172 #[doc(hidden)]
178 pub fn external_payload<Type>(
179 &self,
180 value: Value<Type, ProviderValueContext<'call, Type::Host>>,
181 ) -> ProviderExternalItem<Type>
182 where
183 Type: ProviderExternalCodec<Profile>,
184 {
185 Type::input(&self.context.call, value.into_host())
186 }
187
188 pub fn store_dynamic<Value, Owner>(&mut self, value: Value) -> StoredDynamic<Owner>
190 where
191 Value: ProviderDynamicValue<'call, Profile, Provider, Return>,
192 Owner: ProviderStoredOwner,
193 {
194 let value = value.into_host(&mut self.context.call);
195 StoredDynamic::new(
196 self.context
197 .call
198 .provider_store_dynamic::<Value::Host>(value),
199 )
200 }
201
202 pub fn restore_dynamic<Type, Owner>(
205 &mut self,
206 value: &StoredDynamic<Owner>,
207 ) -> Option<Type::View<'call>>
208 where
209 Type: ProviderDynamicInput<Profile, Provider, Return>,
210 Owner: ProviderStoredOwner,
211 {
212 let value = self
213 .context
214 .call
215 .provider_restore_dynamic::<Type::Host>(value.host())?;
216 Some(Type::from_host(&mut self.context.call, value))
217 }
218
219 pub fn restore_dynamic_value<Type, Host, Owner>(
222 &mut self,
223 value: &StoredDynamic<Owner>,
224 _type_witness: &Value<Type, ProviderValueContext<'call, Host>>,
225 ) -> Option<Value<Type, ProviderValueContext<'call, Host>>>
226 where
227 Host: HostType,
228 Owner: ProviderStoredOwner,
229 {
230 self.context
231 .call
232 .provider_restore_dynamic::<Host>(value.host())
233 .map(Value::from_host)
234 }
235
236 pub fn invoke<Signature, Codec>(
238 &mut self,
239 callback: Callback<
240 Signature,
241 ProviderCallbackContext<'call, Profile, Provider, Return, Codec>,
242 >,
243 arguments: Codec::Arguments,
244 ) -> HostResult<Codec::Returned>
245 where
246 Codec: ProviderCallbackCodec<'call, Profile, Provider, Return>,
247 {
248 callback.invoke(&mut self.context.call, arguments)
249 }
250
251 #[doc(hidden)]
252 pub fn from_host_call(call: HostCall<'call, Profile, Provider, Return>) -> Self {
253 Self {
254 context: ProviderActiveCall { call },
255 state: PhantomData,
256 }
257 }
258
259 #[doc(hidden)]
260 pub fn into_host_call(self) -> HostCall<'call, Profile, Provider, Return> {
261 self.context.call
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::{Call, HostResult};
268 use crate::host::CallArguments;
269 use crate::host::HostCallErrorKind;
270 use crate::host::test::{TestHostCallRuntime, TestHostProfile, TestRunState};
271 use crate::host::{
272 HostCallable, HostFunctionToken, HostScopedValue, HostTypeList, HostTypeListEnd,
273 HostTypeParameter, HostValue, HostValueFamily, HostValueToken,
274 };
275 use crate::provider::{
276 Callback, ProviderCallbackCodec, ProviderCallbackContext, ProviderConstructions,
277 ProviderNoConstructions, ProviderValueContext, Value,
278 };
279 use crate::{HostCall, HostFailure, HostProvider};
280 use num_bigint::BigInt;
281
282 struct Provider;
283
284 impl HostProvider<TestHostProfile> for Provider {
285 type State = TestRunState;
286
287 fn project(state: &mut TestRunState) -> &mut Self::State {
288 state
289 }
290 }
291
292 struct IntCallbackCodec;
293
294 impl<'call> ProviderCallbackCodec<'call, TestHostProfile, Provider, BigInt> for IntCallbackCodec {
295 type HostArguments = HostTypeList<BigInt, HostTypeListEnd>;
296 type HostReturn = BigInt;
297 type Arguments = (BigInt,);
298 type Returned = BigInt;
299 type Requirements = ProviderNoConstructions;
300
301 fn into_host_arguments(
302 arguments: Self::Arguments,
303 _call: &mut HostCall<'call, TestHostProfile, Provider, BigInt>,
304 _constructions: &ProviderConstructions<'call, Self::Requirements>,
305 ) -> <Self::HostArguments as crate::HostTypeSequence>::Values<'call> {
306 (arguments.0, ())
307 }
308
309 fn from_host_return(
310 value: <Self::HostReturn as crate::HostType>::Value<'call>,
311 _call: &mut HostCall<'call, TestHostProfile, Provider, BigInt>,
312 ) -> Self::Returned {
313 value
314 }
315 }
316
317 #[test]
318 fn shared_call_exposes_only_the_borrowed_provider_state() {
319 let state = TestRunState {
320 counter: 7,
321 unrelated: true,
322 };
323 let call = Call::from_shared_state(&state);
324
325 assert_eq!(call.state().counter, 7);
326 assert!(call.state().unrelated);
327 }
328
329 #[test]
330 fn active_call_projects_shared_and_mutable_provider_state() {
331 let mut state = TestRunState::default();
332 {
333 let mut runtime =
334 TestHostCallRuntime::new(&mut state, CallArguments::new(Vec::new(), Vec::new()));
335 let host_call = HostCall::<TestHostProfile, Provider, bool>::new(&mut runtime);
336 let mut call = Call::from_host_call(host_call);
337
338 assert_eq!(call.state().counter, 0);
339 call.state_mut().counter = 3;
340 assert_eq!(call.state().counter, 3);
341
342 let _recovered_call = call.into_host_call();
343 }
344 assert_eq!(state.counter, 3);
345 }
346
347 #[test]
348 fn host_result_preserves_the_local_failure_envelope() {
349 fn fail() -> HostResult<()> {
350 Err(HostFailure::new("provider unavailable").into())
351 }
352
353 assert_eq!(
354 fail()
355 .expect_err("host failure should stop the call")
356 .into_kind(),
357 HostCallErrorKind::Failure(HostFailure::new("provider unavailable")),
358 );
359 }
360
361 #[test]
362 fn active_call_owns_generic_source_semantics_without_materializing_values() {
363 type Parameter = HostTypeParameter<0>;
364 let mut state = TestRunState::default();
365 let mut runtime =
366 TestHostCallRuntime::new(&mut state, CallArguments::new(Vec::new(), Vec::new()));
367 let host = HostValue::<Parameter>::new(HostValueToken {
368 family: HostValueFamily::String,
369 index: 2,
370 });
371 let left = Value::<Parameter, ProviderValueContext<'_, Parameter>>::from_host(host);
372 let right = Value::<Parameter, ProviderValueContext<'_, Parameter>>::from_host(host);
373 let host_call = HostCall::<TestHostProfile, Provider, bool>::new(&mut runtime);
374 let call = Call::from_host_call(host_call);
375
376 assert!(!call.equal(&left, &right));
377 assert_eq!(call.source_hash(&left), 17);
378 assert_eq!(call.inspect(&left), "inspected");
379 }
380
381 #[test]
382 fn active_call_invokes_one_static_callback_codec() {
383 type Context<'call> =
384 ProviderCallbackContext<'call, TestHostProfile, Provider, BigInt, IntCallbackCodec>;
385 let mut state = TestRunState::default();
386 let mut runtime =
387 TestHostCallRuntime::new(&mut state, CallArguments::new(Vec::new(), Vec::new()));
388 let host_call = HostCall::<TestHostProfile, Provider, BigInt>::new(&mut runtime);
389 let mut call = Call::from_host_call(host_call);
390 let constructions = ProviderConstructions::none();
391 let constructions = Clone::clone(&constructions);
392 let callback = Callback::<fn(BigInt) -> BigInt, Context<'_>>::from_host(
393 HostCallable::new(HostFunctionToken(3)),
394 constructions,
395 );
396 let callback = Clone::clone(&callback);
397
398 let returned = call
399 .invoke(callback, (BigInt::from(7),))
400 .expect("typed callback should invoke through the active call");
401 assert_eq!(returned, BigInt::from(0));
402 assert_eq!(runtime.completed(), Some(&HostScopedValue::Int(7.into())));
403 }
404}