Skip to main content

geam_core/host/
profile.rs

1use crate::host::{
2    HostCallArguments, HostCallCompletion, HostConstruction, HostCustom, HostCustomArgumentSlot,
3    HostCustomConstructor, HostCustomType, HostExternal, HostExternalArgumentSlot,
4    HostExternalBinding, HostExternalPayloadBuilder, HostExternalPayloadView, HostExternalSchema,
5    HostExternalStorage, HostExternalType, HostFunctionArgumentSlot, HostList,
6    HostListArgumentSlot, HostListType, HostStoredValue, HostTuple, HostTupleArgumentSlot,
7    HostTupleType, HostType, HostTypeSequence, HostValue, HostValueArgumentSlot,
8};
9use crate::provider::{
10    List, ProviderExternalPayloadAccess, ProviderListContext, ProviderListItemDecoder,
11};
12use std::marker::PhantomData;
13
14mod runtime;
15
16pub(crate) use runtime::HostCallRuntime;
17#[cfg(test)]
18pub(crate) use runtime::test;
19
20pub trait HostProfile: Send + Sync + 'static {
21    type RunState;
22    type ExternalStores: Default + 'static;
23}
24
25pub trait HostProvider<Profile: HostProfile>: Send + Sync + 'static {
26    type State;
27
28    fn project(state: &mut Profile::RunState) -> &mut Self::State;
29}
30
31type BoundExternalStorage<Profile, Provider, Schema> =
32    <Provider as HostExternalBinding<Profile, Schema>>::Storage;
33type BoundExternalPayload<Profile, Provider, Schema> = <BoundExternalStorage<
34    Profile,
35    Provider,
36    Schema,
37> as HostExternalStorage<Profile, Schema>>::Payload;
38
39#[derive(Debug, Clone, Copy, Default)]
40pub struct StatelessHostProfile;
41
42pub struct HostCall<'call, Profile, Provider, Return>
43where
44    Profile: HostProfile,
45    Provider: HostProvider<Profile>,
46    Return: HostType,
47{
48    runtime: &'call mut dyn HostCallRuntime<Profile>,
49    marker: PhantomData<(Provider, Return)>,
50}
51
52impl HostProfile for StatelessHostProfile {
53    type RunState = ();
54    type ExternalStores = ();
55}
56
57impl<'call, Profile, Provider, Return> HostCall<'call, Profile, Provider, Return>
58where
59    Profile: HostProfile,
60    Provider: HostProvider<Profile>,
61    Return: HostType,
62{
63    pub(crate) fn new(runtime: &'call mut dyn HostCallRuntime<Profile>) -> Self {
64        Self {
65            runtime,
66            marker: PhantomData,
67        }
68    }
69
70    pub fn state(&mut self) -> &mut Provider::State {
71        Provider::project(self.runtime.state())
72    }
73
74    pub fn return_value(self, value: Return::Value<'call>) -> HostCallCompletion<'call, Return> {
75        HostCallCompletion::new(
76            self.runtime
77                .complete(crate::host::type_::into_scoped::<Return>(value)),
78        )
79    }
80
81    pub fn equal<Type: HostType>(
82        &self,
83        left: Type::Value<'call>,
84        right: Type::Value<'call>,
85    ) -> bool {
86        self.runtime.equal(
87            crate::host::type_::into_scoped::<Type>(left),
88            crate::host::type_::into_scoped::<Type>(right),
89        )
90    }
91
92    /// Hashes a call-scoped value consistently with Gleam source equality.
93    ///
94    /// The result is intended for runtime lookup within this execution. It is
95    /// not a stable serialization or a process-independent value.
96    pub fn source_hash<Type: HostType>(&self, value: Type::Value<'call>) -> u64 {
97        self.runtime
98            .source_hash(crate::host::type_::into_scoped::<Type>(value))
99    }
100
101    /// Returns the canonical Gleam-facing inspection of a call-scoped value.
102    pub fn inspect<Type: HostType>(&self, value: Type::Value<'call>) -> ecow::EcoString {
103        self.runtime
104            .inspect(crate::host::type_::into_scoped::<Type>(value))
105    }
106
107    pub(crate) fn arguments(&self) -> &dyn HostCallArguments {
108        self.runtime.arguments()
109    }
110
111    pub(crate) fn value<Type>(&self, slot: HostValueArgumentSlot) -> HostValue<'call, Type> {
112        HostValue::new(self.runtime.value(slot))
113    }
114
115    pub(crate) fn list<Item>(&self, slot: HostListArgumentSlot) -> HostList<'call, Item> {
116        HostList::new(self.runtime.list(slot))
117    }
118
119    pub(crate) fn tuple<Elements>(
120        &self,
121        slot: HostTupleArgumentSlot,
122    ) -> HostTuple<'call, Elements> {
123        HostTuple::new(self.runtime.tuple(slot))
124    }
125
126    pub(crate) fn custom<Custom>(&self, slot: HostCustomArgumentSlot) -> HostCustom<'call, Custom> {
127        HostCustom::new(self.runtime.custom(slot))
128    }
129
130    pub(crate) fn external<Type>(
131        &self,
132        slot: HostExternalArgumentSlot,
133    ) -> HostExternal<'call, Type> {
134        HostExternal::new(self.runtime.external(slot))
135    }
136
137    pub(crate) fn function<Arguments, FunctionReturn>(
138        &self,
139        slot: HostFunctionArgumentSlot,
140    ) -> crate::host::HostCallable<'call, Arguments, FunctionReturn> {
141        crate::host::HostCallable::new(self.runtime.function(slot))
142    }
143
144    pub fn list_len<Item>(&self, value: HostList<'call, Item>) -> usize {
145        self.runtime.list_len(value.token)
146    }
147
148    pub fn list_item<Item: HostType>(
149        &mut self,
150        value: HostList<'call, Item>,
151        index: usize,
152    ) -> Option<Item::Value<'call>> {
153        self.runtime
154            .list_item(value.token, index)
155            .map(|token| crate::host::type_::from_token::<Item, Profile>(self.runtime, token))
156    }
157
158    #[doc(hidden)]
159    pub fn provider_list<Item, HostItem, Decoder>(
160        &self,
161        value: HostList<'call, HostItem>,
162        decoder: Decoder,
163    ) -> List<Item, ProviderListContext<'call, HostItem, Decoder>>
164    where
165        HostItem: HostType,
166        Decoder: ProviderListItemDecoder<Item>,
167    {
168        let retained = self.runtime.retain_list(value.token);
169        ProviderListContext::new(value, retained, decoder).into_list()
170    }
171
172    #[doc(hidden)]
173    pub fn provider_input_list<Item, HostItem, Decoder>(
174        &self,
175        value: HostList<'call, HostItem>,
176        decoder: Decoder,
177    ) -> List<Item, crate::provider::ProviderInputListContext<Decoder>>
178    where
179        HostItem: HostType,
180        Decoder: ProviderListItemDecoder<Item>,
181    {
182        crate::provider::ProviderInputListContext::new(
183            self.runtime.retain_list(value.token),
184            decoder,
185        )
186    }
187
188    #[doc(hidden)]
189    pub fn provider_external_item_with<Binding, Schema, Arguments>(
190        &self,
191        value: HostExternal<'call, HostExternalType<Schema, Arguments>>,
192    ) -> crate::provider::ProviderExternalItem<BoundExternalPayload<Profile, Binding, Schema>>
193    where
194        Schema: HostExternalSchema,
195        Binding: HostExternalBinding<Profile, Schema>,
196        Arguments: HostTypeSequence,
197    {
198        let lease = self.runtime.external_lease(value.token);
199        crate::provider::ProviderExternalItem::new(
200            BoundExternalStorage::<Profile, Binding, Schema>::store(self.runtime.external_stores())
201                .view(&lease),
202            lease,
203        )
204    }
205
206    #[doc(hidden)]
207    pub fn provider_external_from_item<Schema, Arguments, Payload>(
208        &mut self,
209        value: crate::provider::ProviderExternalItem<Payload>,
210    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
211    where
212        Schema: HostExternalSchema,
213        Arguments: HostTypeSequence,
214    {
215        HostExternal::new(self.runtime.build_external(
216            &crate::host::HostTypeDescriptor::of::<HostExternalType<Schema, Arguments>>(),
217            value.into_lease(),
218        ))
219    }
220
221    #[doc(hidden)]
222    pub fn provider_external_payload_access_with<Binding, Schema>(
223        &self,
224    ) -> ProviderExternalPayloadAccess<BoundExternalPayload<Profile, Binding, Schema>>
225    where
226        Schema: HostExternalSchema,
227        Binding: HostExternalBinding<Profile, Schema>,
228    {
229        ProviderExternalPayloadAccess::new(BoundExternalStorage::<Profile, Binding, Schema>::store(
230            self.runtime.external_stores(),
231        ))
232    }
233
234    /// Constructs a list authorized by one registered construction token.
235    pub fn construct_list<Item: HostType>(
236        &mut self,
237        _construction: HostConstruction<'call, HostListType<Item>>,
238        values: impl IntoIterator<Item = Item::Value<'call>>,
239    ) -> HostList<'call, Item> {
240        let values = values
241            .into_iter()
242            .map(crate::host::type_::into_scoped::<Item>)
243            .collect::<Vec<_>>()
244            .into_boxed_slice();
245        let value = self.runtime.build_list(
246            &crate::host::HostTypeDescriptor::of::<HostListType<Item>>(),
247            values,
248        );
249        HostList::new(self.runtime.list_token(value))
250    }
251
252    pub fn tuple_len<Elements>(&self, value: HostTuple<'call, Elements>) -> usize {
253        self.runtime.tuple_len(value.token)
254    }
255
256    pub fn tuple_values<Elements: HostTypeSequence>(
257        &mut self,
258        value: HostTuple<'call, Elements>,
259    ) -> Elements::Values<'call> {
260        let values = self.runtime.tuple_values(value.token);
261        crate::host::type_::from_tokens::<Elements, Profile>(self.runtime, &values)
262    }
263
264    /// Constructs a tuple authorized by one registered construction token.
265    pub fn construct_tuple<Elements: HostTypeSequence>(
266        &mut self,
267        _construction: HostConstruction<'call, HostTupleType<Elements>>,
268        values: Elements::Values<'call>,
269    ) -> HostTuple<'call, Elements> {
270        let mut output = Vec::new();
271        crate::host::type_::into_scoped_values::<Elements>(values, &mut output);
272        let value = self.runtime.build_tuple(output.into_boxed_slice());
273        HostTuple::new(self.runtime.tuple_token(value))
274    }
275
276    pub fn custom_constructor<Custom>(&self, value: HostCustom<'call, Custom>) -> usize {
277        self.runtime.custom_constructor(value.token)
278    }
279
280    pub fn custom_fields<Constructor>(
281        &mut self,
282        value: HostCustom<'call, Constructor::Custom>,
283    ) -> Option<<Constructor::Fields as HostTypeSequence>::Values<'call>>
284    where
285        Constructor: HostCustomConstructor,
286    {
287        if self.runtime.custom_constructor(value.token)
288            != crate::host::type_::custom_constructor_index::<Constructor>()
289        {
290            return None;
291        }
292        let fields = self.runtime.custom_fields(value.token);
293        Some(crate::host::type_::from_tokens::<
294            Constructor::Fields,
295            Profile,
296        >(self.runtime, &fields))
297    }
298
299    #[doc(hidden)]
300    pub fn provider_custom_fields<Constructor>(
301        &mut self,
302        value: HostCustom<'call, Constructor::Custom>,
303    ) -> Option<<Constructor::Fields as HostTypeSequence>::Values<'call>>
304    where
305        Constructor: HostCustomConstructor,
306    {
307        if self.runtime.custom_constructor(value.token)
308            != crate::host::type_::custom_constructor_index::<Constructor>()
309        {
310            return None;
311        }
312        let fields = self.runtime.take_custom_fields(value.token);
313        Some(crate::host::type_::from_tokens::<
314            Constructor::Fields,
315            Profile,
316        >(self.runtime, &fields))
317    }
318
319    /// Takes the fields of the constructor left after generated code has
320    /// excluded every preceding constructor in the linked schema.
321    #[doc(hidden)]
322    pub fn provider_remaining_custom_fields<Constructor>(
323        &mut self,
324        value: HostCustom<'call, Constructor::Custom>,
325    ) -> <Constructor::Fields as HostTypeSequence>::Values<'call>
326    where
327        Constructor: HostCustomConstructor,
328    {
329        let fields = self.runtime.take_custom_fields(value.token);
330        crate::host::type_::from_tokens::<Constructor::Fields, Profile>(self.runtime, &fields)
331    }
332
333    /// Constructs an ordinary custom value authorized by one registered type token.
334    pub fn construct_custom<Constructor>(
335        &mut self,
336        _construction: HostConstruction<'call, Constructor::Custom>,
337        fields: <Constructor::Fields as HostTypeSequence>::Values<'call>,
338    ) -> HostCustom<'call, Constructor::Custom>
339    where
340        Constructor: HostCustomConstructor,
341        Constructor::Fields: HostTypeSequence,
342    {
343        let mut output = Vec::new();
344        crate::host::type_::into_scoped_values::<Constructor::Fields>(fields, &mut output);
345        let value = self.runtime.build_custom(
346            &crate::host::HostTypeDescriptor::of::<Constructor::Custom>(),
347            crate::host::type_::custom_constructor_index::<Constructor>(),
348            output.into_boxed_slice(),
349        );
350        HostCustom::new(self.runtime.custom_token(value))
351    }
352
353    /// Borrows the Rust payload behind one typed external value.
354    pub fn external_payload<Schema, Arguments>(
355        &self,
356        value: HostExternal<'call, HostExternalType<Schema, Arguments>>,
357    ) -> HostExternalPayloadView<'call, BoundExternalPayload<Profile, Provider, Schema>, Arguments>
358    where
359        Schema: HostExternalSchema,
360        Provider: HostExternalBinding<Profile, Schema>,
361        Arguments: HostTypeSequence,
362    {
363        self.external_payload_with::<Provider, Schema, Arguments>(value)
364    }
365
366    #[doc(hidden)]
367    pub fn external_payload_with<Binding, Schema, Arguments>(
368        &self,
369        value: HostExternal<'call, HostExternalType<Schema, Arguments>>,
370    ) -> HostExternalPayloadView<'call, BoundExternalPayload<Profile, Binding, Schema>, Arguments>
371    where
372        Schema: HostExternalSchema,
373        Binding: HostExternalBinding<Profile, Schema>,
374        Arguments: HostTypeSequence,
375    {
376        let lease = self.runtime.external_lease(value.token);
377        HostExternalPayloadView::new(
378            BoundExternalStorage::<Profile, Binding, Schema>::store(self.runtime.external_stores())
379                .view(&lease),
380        )
381    }
382
383    pub(crate) fn restore_stored<Type, Stored>(
384        &mut self,
385        value: &HostStoredValue<Stored>,
386    ) -> Type::Value<'call>
387    where
388        Type: HostType,
389    {
390        self.restore_runtime_value::<Type>(&value.value)
391    }
392
393    #[doc(hidden)]
394    pub fn provider_store<Stored, Type>(
395        &mut self,
396        value: Type::Value<'call>,
397    ) -> HostStoredValue<Stored>
398    where
399        Type: HostType,
400    {
401        HostStoredValue::new(
402            self.runtime
403                .retain_stored(crate::host::type_::into_scoped::<Type>(value)),
404        )
405    }
406
407    #[doc(hidden)]
408    pub fn provider_restore<Type, Stored>(
409        &mut self,
410        value: &HostStoredValue<Stored>,
411    ) -> Type::Value<'call>
412    where
413        Type: HostType,
414    {
415        self.restore_stored::<Type, Stored>(value)
416    }
417
418    #[doc(hidden)]
419    pub fn provider_store_dynamic<Type>(
420        &mut self,
421        value: Type::Value<'call>,
422    ) -> crate::HostStoredDynamic
423    where
424        Type: HostType,
425    {
426        crate::HostStoredDynamic::new(
427            self.runtime
428                .retain_stored(crate::host::type_::into_scoped::<Type>(value)),
429        )
430    }
431
432    #[doc(hidden)]
433    pub fn provider_restore_dynamic<Type>(
434        &mut self,
435        value: &crate::HostStoredDynamic,
436    ) -> Option<Type::Value<'call>>
437    where
438        Type: HostType,
439    {
440        value.decode::<Profile, Provider, Return, Type>(self)
441    }
442
443    pub(in crate::host) fn restore_runtime_value<Type>(
444        &mut self,
445        value: &crate::runtime::StoredRuntimeValue,
446    ) -> Type::Value<'call>
447    where
448        Type: HostType,
449    {
450        let token = self.runtime.restore_stored(value);
451        crate::host::type_::from_token::<Type, Profile>(self.runtime, token)
452    }
453
454    pub(in crate::host) fn resolve_host_type<Type: HostType>(
455        &self,
456    ) -> Option<crate::plan::ValueType> {
457        self.runtime
458            .resolve_host_type(&crate::host::HostTypeDescriptor::of::<Type>())
459    }
460
461    /// Invokes a Gleam callable while this host call owns the active runtime.
462    ///
463    /// A provider-state borrow must end before re-entry.
464    ///
465    /// ```compile_fail
466    /// use geam_core::{
467    ///     HostCall, HostCallCompletion, HostCallError, HostCallable, HostProfile, HostProvider,
468    ///     HostTypeList, HostTypeListEnd,
469    /// };
470    /// use num_bigint::BigInt;
471    ///
472    /// struct Profile;
473    /// struct Provider;
474    ///
475    /// impl HostProfile for Profile {
476    ///     type RunState = usize;
477    ///     type ExternalStores = ();
478    /// }
479    ///
480    /// impl HostProvider<Profile> for Provider {
481    ///     type State = usize;
482    ///
483    ///     fn project(state: &mut usize) -> &mut Self::State {
484    ///         state
485    ///     }
486    /// }
487    ///
488    /// type Arguments = HostTypeList<BigInt, HostTypeListEnd>;
489    ///
490    /// fn reenter_with_live_state<'call>(
491    ///     mut call: HostCall<'call, Profile, Provider, BigInt>,
492    ///     callable: HostCallable<'call, Arguments, BigInt>,
493    /// ) -> Result<HostCallCompletion<'call, BigInt>, HostCallError> {
494    ///     let state = call.state();
495    ///     let returned = call.invoke(callable, (BigInt::from(1), ()))?;
496    ///     *state += 1;
497    ///     Ok(call.return_value(returned))
498    /// }
499    /// ```
500    pub fn invoke<Arguments, FunctionReturn>(
501        &mut self,
502        function: crate::host::HostCallable<'call, Arguments, FunctionReturn>,
503        arguments: Arguments::Values<'call>,
504    ) -> Result<FunctionReturn::Value<'call>, crate::HostCallError>
505    where
506        Arguments: HostTypeSequence,
507        FunctionReturn: HostType,
508    {
509        let mut values = Vec::new();
510        crate::host::type_::into_scoped_values::<Arguments>(arguments, &mut values);
511        let returned = self
512            .runtime
513            .invoke(function.token, values.into_boxed_slice())?;
514        Ok(crate::host::type_::from_token::<FunctionReturn, Profile>(
515            self.runtime,
516            returned,
517        ))
518    }
519
520    /// Constructs an intermediate external payload that retains typed Gleam values.
521    pub fn construct_external_with<Schema, Arguments>(
522        &mut self,
523        _construction: HostConstruction<'call, HostExternalType<Schema, Arguments>>,
524        build: impl FnOnce(
525            &mut HostExternalPayloadBuilder<'_, Profile, Arguments>,
526        ) -> BoundExternalPayload<Profile, Provider, Schema>,
527    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
528    where
529        Schema: HostExternalSchema,
530        Provider: HostExternalBinding<Profile, Schema>,
531        Arguments: HostTypeSequence,
532        HostExternalType<Schema, Arguments>: HostType,
533    {
534        let value = {
535            let mut builder = HostExternalPayloadBuilder::new(self.runtime);
536            build(&mut builder)
537        };
538        let lease = self.insert_external_payload_with::<Provider, Schema, Arguments>(value);
539        HostExternal::new(self.runtime.build_external(
540            &crate::host::HostTypeDescriptor::of::<HostExternalType<Schema, Arguments>>(),
541            lease,
542        ))
543    }
544
545    /// Constructs a retained external payload through its declaration-owned
546    /// binding rather than the provider handling the active function.
547    #[doc(hidden)]
548    pub fn construct_retained_external_with_binding<Binding, Schema, Arguments>(
549        &mut self,
550        _construction: HostConstruction<'call, HostExternalType<Schema, Arguments>>,
551        build: impl FnOnce(
552            &mut HostExternalPayloadBuilder<'_, Profile, Arguments>,
553        ) -> BoundExternalPayload<Profile, Binding, Schema>,
554    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
555    where
556        Schema: HostExternalSchema,
557        Binding: HostExternalBinding<Profile, Schema>,
558        Arguments: HostTypeSequence,
559        HostExternalType<Schema, Arguments>: HostType,
560    {
561        let value = {
562            let mut builder = HostExternalPayloadBuilder::new(self.runtime);
563            build(&mut builder)
564        };
565        self.seal_constructed_external_with::<Binding, Schema, Arguments>(value)
566    }
567}
568
569impl<'call, Profile, Provider, Schema, Arguments>
570    HostCall<'call, Profile, Provider, HostExternalType<Schema, Arguments>>
571where
572    Profile: HostProfile,
573    Provider: HostProvider<Profile>,
574    Schema: HostExternalSchema,
575    Arguments: HostTypeSequence,
576    HostExternalType<Schema, Arguments>: HostType,
577{
578    pub fn create_external(
579        &mut self,
580        value: BoundExternalPayload<Profile, Provider, Schema>,
581    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
582    where
583        Provider: HostExternalBinding<Profile, Schema>,
584    {
585        self.create_external_with_binding::<Provider>(value)
586    }
587
588    #[doc(hidden)]
589    pub fn create_external_with_binding<Binding>(
590        &mut self,
591        value: BoundExternalPayload<Profile, Binding, Schema>,
592    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
593    where
594        Binding: HostExternalBinding<Profile, Schema>,
595    {
596        self.seal_external_payload_with::<Binding>(value)
597    }
598
599    /// Creates an external payload that may retain typed Gleam values.
600    pub fn create_external_with(
601        &mut self,
602        build: impl FnOnce(
603            &mut HostExternalPayloadBuilder<'_, Profile, Arguments>,
604        ) -> BoundExternalPayload<Profile, Provider, Schema>,
605    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
606    where
607        Provider: HostExternalBinding<Profile, Schema>,
608    {
609        let value = {
610            let mut builder = HostExternalPayloadBuilder::new(self.runtime);
611            build(&mut builder)
612        };
613        self.seal_external_payload_with::<Provider>(value)
614    }
615
616    fn seal_external_payload_with<Binding>(
617        &mut self,
618        value: BoundExternalPayload<Profile, Binding, Schema>,
619    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
620    where
621        Binding: HostExternalBinding<Profile, Schema>,
622    {
623        let lease = self.insert_external_payload_with::<Binding, Schema, Arguments>(value);
624        HostExternal::new(self.runtime.build_external(
625            &crate::host::HostTypeDescriptor::of::<HostExternalType<Schema, Arguments>>(),
626            lease,
627        ))
628    }
629}
630
631impl<'call, Profile, Provider, Return> HostCall<'call, Profile, Provider, Return>
632where
633    Profile: HostProfile,
634    Provider: HostProvider<Profile>,
635    Return: HostType,
636{
637    /// Constructs an intermediate external payload authorized by one registered type token.
638    pub fn construct_external<Schema, Arguments>(
639        &mut self,
640        _construction: HostConstruction<'call, HostExternalType<Schema, Arguments>>,
641        value: BoundExternalPayload<Profile, Provider, Schema>,
642    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
643    where
644        Schema: HostExternalSchema,
645        Provider: HostExternalBinding<Profile, Schema>,
646        Arguments: HostTypeSequence,
647        HostExternalType<Schema, Arguments>: HostType,
648    {
649        self.construct_external_with_binding::<Provider, Schema, Arguments>(_construction, value)
650    }
651
652    #[doc(hidden)]
653    pub fn construct_external_with_binding<Binding, Schema, Arguments>(
654        &mut self,
655        _construction: HostConstruction<'call, HostExternalType<Schema, Arguments>>,
656        value: BoundExternalPayload<Profile, Binding, Schema>,
657    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
658    where
659        Schema: HostExternalSchema,
660        Binding: HostExternalBinding<Profile, Schema>,
661        Arguments: HostTypeSequence,
662        HostExternalType<Schema, Arguments>: HostType,
663    {
664        self.seal_constructed_external_with::<Binding, Schema, Arguments>(value)
665    }
666
667    fn seal_constructed_external_with<Binding, Schema, Arguments>(
668        &mut self,
669        value: BoundExternalPayload<Profile, Binding, Schema>,
670    ) -> HostExternal<'call, HostExternalType<Schema, Arguments>>
671    where
672        Schema: HostExternalSchema,
673        Binding: HostExternalBinding<Profile, Schema>,
674        Arguments: HostTypeSequence,
675        HostExternalType<Schema, Arguments>: HostType,
676    {
677        let lease = self.insert_external_payload_with::<Binding, Schema, Arguments>(value);
678        HostExternal::new(self.runtime.build_external(
679            &crate::host::HostTypeDescriptor::of::<HostExternalType<Schema, Arguments>>(),
680            lease,
681        ))
682    }
683
684    fn insert_external_payload_with<Binding, Schema, Arguments>(
685        &self,
686        value: BoundExternalPayload<Profile, Binding, Schema>,
687    ) -> crate::host::ExternalPayloadLease
688    where
689        Schema: HostExternalSchema,
690        Binding: HostExternalBinding<Profile, Schema>,
691        Arguments: HostTypeSequence,
692        HostExternalType<Schema, Arguments>: HostType,
693    {
694        BoundExternalStorage::<Profile, Binding, Schema>::store(self.runtime.external_stores())
695            .insert(
696                value,
697                BoundExternalStorage::<Profile, Binding, Schema>::source_equal,
698                BoundExternalStorage::<Profile, Binding, Schema>::source_hash,
699                BoundExternalStorage::<Profile, Binding, Schema>::inspect,
700            )
701    }
702}
703
704impl<'call, Profile, Provider, Item> HostCall<'call, Profile, Provider, HostListType<Item>>
705where
706    Profile: HostProfile,
707    Provider: HostProvider<Profile>,
708    Item: HostType,
709{
710    pub fn return_list(
711        self,
712        values: impl IntoIterator<Item = Item::Value<'call>>,
713    ) -> HostCallCompletion<'call, HostListType<Item>> {
714        let values = values
715            .into_iter()
716            .map(crate::host::type_::into_scoped::<Item>)
717            .collect::<Vec<_>>()
718            .into_boxed_slice();
719        HostCallCompletion::new(self.runtime.build_list(
720            &crate::host::HostTypeDescriptor::of::<HostListType<Item>>(),
721            values,
722        ))
723    }
724}
725
726impl<'call, Profile, Provider, Elements> HostCall<'call, Profile, Provider, HostTupleType<Elements>>
727where
728    Profile: HostProfile,
729    Provider: HostProvider<Profile>,
730    Elements: HostTypeSequence,
731{
732    pub fn return_tuple(
733        self,
734        values: <Elements as crate::host::HostTypeSequence>::Values<'call>,
735    ) -> HostCallCompletion<'call, HostTupleType<Elements>> {
736        let mut output = Vec::new();
737        crate::host::type_::into_scoped_values::<Elements>(values, &mut output);
738        HostCallCompletion::new(self.runtime.build_tuple(output.into_boxed_slice()))
739    }
740}
741
742impl<'call, Profile, Provider, Schema, Arguments>
743    HostCall<'call, Profile, Provider, HostCustomType<Schema, Arguments>>
744where
745    Profile: HostProfile,
746    Provider: HostProvider<Profile>,
747    Schema: crate::host::HostCustomSchema,
748    Arguments: HostTypeSequence,
749{
750    pub fn return_custom<Constructor>(
751        self,
752        fields: <Constructor::Fields as crate::host::HostTypeSequence>::Values<'call>,
753    ) -> HostCallCompletion<'call, HostCustomType<Schema, Arguments>>
754    where
755        Constructor: HostCustomConstructor<Custom = HostCustomType<Schema, Arguments>>,
756        Constructor::Fields: HostTypeSequence,
757    {
758        let mut output = Vec::new();
759        crate::host::type_::into_scoped_values::<Constructor::Fields>(fields, &mut output);
760        HostCallCompletion::new(self.runtime.build_custom(
761            &crate::host::HostTypeDescriptor::of::<HostCustomType<Schema, Arguments>>(),
762            crate::host::type_::custom_constructor_index::<Constructor>(),
763            output.into_boxed_slice(),
764        ))
765    }
766}
767
768#[cfg(test)]
769mod tests {
770    use super::{HostCall, HostProvider};
771    use crate::BitArrayValue;
772    use crate::host::function::CallArguments;
773    use crate::host::test::{
774        StatelessTestProvider, TestHostCallRuntime, TestHostProfile, TestRunState,
775    };
776    use crate::host::{
777        HostCallable, HostConstructions, HostCustom, HostCustomConstructorAt,
778        HostCustomConstructorDefinition, HostCustomConstructorList, HostCustomConstructorListEnd,
779        HostCustomConstructorSchema, HostCustomFieldListEnd, HostCustomFieldSchema,
780        HostCustomIndex0, HostCustomIndexNext, HostCustomSchema, HostCustomToken, HostCustomType,
781        HostCustomTypeSchema, HostFunctionToken, HostFunctionType, HostList, HostListToken,
782        HostListType, HostScopedValue, HostTuple, HostTupleToken, HostTupleType, HostTypeIndex0,
783        HostTypeIndexNext, HostTypeList, HostTypeListEnd, HostTypeParameter, HostValue,
784        HostValueFamily, HostValueToken, StatelessHostProfile,
785    };
786    use crate::provider::{ProviderListItemDecoder, ProviderListItemValue};
787    use ecow::EcoString;
788    use num_bigint::BigInt;
789
790    struct Counter;
791
792    struct IntListDecoder;
793
794    impl ProviderListItemDecoder<BigInt> for IntListDecoder {
795        type View = BigInt;
796
797        fn decode(&self, value: ProviderListItemValue) -> Self::View {
798            value.into_scalar()
799        }
800    }
801
802    impl HostProvider<TestHostProfile> for Counter {
803        type State = usize;
804
805        fn project(state: &mut TestRunState) -> &mut Self::State {
806            &mut state.counter
807        }
808    }
809
810    struct MarkerSchema;
811
812    struct MarkerConstructor;
813
814    impl HostCustomConstructorDefinition for MarkerConstructor {
815        const NAME: &'static str = "Marker";
816
817        type Fields = HostCustomFieldListEnd;
818    }
819
820    struct OtherConstructor;
821
822    impl HostCustomConstructorDefinition for OtherConstructor {
823        const NAME: &'static str = "Other";
824
825        type Fields = HostCustomFieldListEnd;
826    }
827
828    impl HostCustomSchema for MarkerSchema {
829        const PACKAGE: &'static str = "domain";
830        const MODULE: &'static str = "domain/marker";
831        const NAME: &'static str = "Marker";
832        const PARAMETER_COUNT: usize = 0;
833
834        type Constructors = HostCustomConstructorList<
835            MarkerConstructor,
836            HostCustomConstructorList<OtherConstructor, HostCustomConstructorListEnd>,
837        >;
838    }
839
840    type MarkerType = HostCustomType<MarkerSchema>;
841    type Marker = HostCustomConstructorAt<MarkerType, HostCustomIndex0, MarkerConstructor>;
842    type Other = HostCustomConstructorAt<
843        MarkerType,
844        HostCustomIndexNext<HostCustomIndex0>,
845        OtherConstructor,
846    >;
847
848    #[test]
849    fn host_call_exposes_only_the_selected_provider_state() {
850        let mut state = TestRunState {
851            counter: 1,
852            unrelated: true,
853        };
854        let arguments = crate::host::function::CallArguments::new(Vec::new(), Vec::new());
855        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
856
857        *HostCall::<TestHostProfile, Counter, bool>::new(&mut runtime).state() += 1;
858
859        assert_eq!(state.counter, 2);
860        assert!(state.unrelated);
861    }
862
863    #[test]
864    fn stateless_provider_projects_the_complete_run_state() {
865        let mut state = ();
866
867        let projected =
868            <StatelessTestProvider as HostProvider<StatelessHostProfile>>::project(&mut state);
869
870        assert_eq!(*projected, ());
871    }
872
873    #[test]
874    fn host_call_reads_and_compares_call_scoped_values() {
875        type EmptyTuple = HostTupleType<HostTypeListEnd>;
876
877        assert_eq!(
878            HostCustomTypeSchema::of::<MarkerSchema>(),
879            HostCustomTypeSchema::new(
880                "domain",
881                "domain/marker",
882                "Marker",
883                0,
884                [
885                    HostCustomConstructorSchema::new("Marker", Vec::<HostCustomFieldSchema>::new(),),
886                    HostCustomConstructorSchema::new("Other", Vec::<HostCustomFieldSchema>::new(),),
887                ],
888            ),
889        );
890        let mut state = TestRunState::default();
891        let arguments = CallArguments::new(Vec::new(), Vec::new());
892        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
893        let mut call = HostCall::<TestHostProfile, Counter, bool>::new(&mut runtime);
894        let list = HostList::<BigInt>::new(HostListToken::Stored(0));
895        let tuple = HostTuple::<HostTypeListEnd>::new(HostTupleToken(0));
896        let custom = HostCustom::<MarkerType>::new(HostCustomToken(0));
897
898        assert_eq!(call.list_len(list), 0);
899        assert_eq!(call.list_item(list, 0), None);
900        assert_eq!(call.tuple_len(tuple), 0);
901        assert_eq!(call.tuple_values::<HostTypeListEnd>(tuple), ());
902        assert_eq!(call.custom_constructor(custom), 0);
903        assert_eq!(call.custom_fields::<Marker>(custom), Some(()),);
904        assert_eq!(call.custom_fields::<Other>(custom), None,);
905        assert_eq!(call.provider_custom_fields::<Marker>(custom), Some(()),);
906        assert_eq!(call.provider_custom_fields::<Other>(custom), None,);
907        assert_eq!(call.provider_remaining_custom_fields::<Marker>(custom), ());
908        assert!(!call.equal::<BigInt>(1.into(), 1.into()));
909        assert!(!call.equal::<HostListType<BigInt>>(list, list));
910        assert!(!call.equal::<EmptyTuple>(tuple, tuple));
911        assert!(!call.equal::<MarkerType>(custom, custom));
912        assert_eq!(call.source_hash::<BigInt>(1.into()), 17);
913        assert_eq!(call.inspect::<BigInt>(1.into()), "inspected");
914    }
915
916    #[test]
917    fn host_call_completes_every_scalar_and_scoped_handle_family() {
918        type Parameter = HostTypeParameter<0>;
919        type List = HostListType<BigInt>;
920        type Tuple = HostTupleType<HostTypeListEnd>;
921
922        let mut state = TestRunState::default();
923        let arguments = CallArguments::new(Vec::new(), Vec::new());
924        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
925        let parameter = HostValue::<Parameter>::new(HostValueToken {
926            family: HostValueFamily::Bool,
927            index: 4,
928        });
929        let list = HostList::<BigInt>::new(HostListToken::Stored(0));
930        let tuple = HostTuple::<HostTypeListEnd>::new(HostTupleToken(0));
931        let custom = HostCustom::<MarkerType>::new(HostCustomToken(0));
932
933        let tokens = [
934            HostCall::<TestHostProfile, Counter, BigInt>::new(&mut runtime)
935                .return_value(1.into())
936                .token,
937            HostCall::<TestHostProfile, Counter, f64>::new(&mut runtime)
938                .return_value(1.5)
939                .token,
940            HostCall::<TestHostProfile, Counter, EcoString>::new(&mut runtime)
941                .return_value("text".into())
942                .token,
943            HostCall::<TestHostProfile, Counter, BitArrayValue>::new(&mut runtime)
944                .return_value(BitArrayValue::from_bytes(vec![1]))
945                .token,
946            HostCall::<TestHostProfile, Counter, char>::new(&mut runtime)
947                .return_value('A')
948                .token,
949            HostCall::<TestHostProfile, Counter, bool>::new(&mut runtime)
950                .return_value(true)
951                .token,
952            HostCall::<TestHostProfile, Counter, ()>::new(&mut runtime)
953                .return_value(())
954                .token,
955            HostCall::<TestHostProfile, Counter, Parameter>::new(&mut runtime)
956                .return_value(parameter)
957                .token,
958            HostCall::<TestHostProfile, Counter, List>::new(&mut runtime)
959                .return_value(list)
960                .token,
961            HostCall::<TestHostProfile, Counter, Tuple>::new(&mut runtime)
962                .return_value(tuple)
963                .token,
964            HostCall::<TestHostProfile, Counter, MarkerType>::new(&mut runtime)
965                .return_value(custom)
966                .token,
967        ];
968
969        assert_eq!(
970            tokens.map(|token| token.family),
971            [
972                HostValueFamily::Int,
973                HostValueFamily::Float,
974                HostValueFamily::String,
975                HostValueFamily::BitArray,
976                HostValueFamily::UtfCodepoint,
977                HostValueFamily::Bool,
978                HostValueFamily::Nil,
979                HostValueFamily::Bool,
980                HostValueFamily::List,
981                HostValueFamily::Tuple,
982                HostValueFamily::Custom,
983            ],
984        );
985        assert_eq!(tokens[7].index, 4);
986    }
987
988    #[test]
989    fn host_call_builds_typed_compound_returns() {
990        type List = HostListType<BigInt>;
991        type Tuple = HostTupleType<HostTypeListEnd>;
992        type Constructions =
993            HostTypeList<List, HostTypeList<Tuple, HostTypeList<MarkerType, HostTypeListEnd>>>;
994        type TupleIndex = HostTypeIndexNext<HostTypeIndex0>;
995        type CustomIndex = HostTypeIndexNext<TupleIndex>;
996
997        let mut state = TestRunState::default();
998        let arguments = CallArguments::new(Vec::new(), Vec::new());
999        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
1000
1001        let list = HostCall::<TestHostProfile, Counter, List>::new(&mut runtime)
1002            .return_list([BigInt::from(1), BigInt::from(2)])
1003            .token;
1004        let mut call = HostCall::<TestHostProfile, Counter, Tuple>::new(&mut runtime);
1005        let constructions = HostConstructions::<Constructions>::new();
1006        let nested_list =
1007            call.construct_list(constructions.at::<HostTypeIndex0>(), [BigInt::from(3)]);
1008        let nested_tuple = call.construct_tuple(constructions.at::<TupleIndex>(), ());
1009        let nested_custom = call.construct_custom::<Marker>(constructions.at::<CustomIndex>(), ());
1010        assert_eq!(nested_list.token, HostListToken::Stored(0));
1011        assert_eq!(nested_tuple.token, HostTupleToken(0));
1012        assert_eq!(nested_custom.token, HostCustomToken(0));
1013        let tuple = call.return_tuple(()).token;
1014        let custom = HostCall::<TestHostProfile, Counter, MarkerType>::new(&mut runtime)
1015            .return_custom::<Marker>(())
1016            .token;
1017
1018        assert_eq!(list.family, HostValueFamily::List);
1019        assert_eq!(tuple.family, HostValueFamily::Tuple);
1020        assert_eq!(custom.family, HostValueFamily::Custom);
1021        assert_eq!(runtime.list_builds(), 2);
1022    }
1023
1024    #[test]
1025    fn returning_an_existing_list_does_not_build_a_new_list() {
1026        type List = HostListType<BigInt>;
1027
1028        let mut state = TestRunState::default();
1029        {
1030            let arguments = CallArguments::new(Vec::new(), Vec::new());
1031            let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
1032            let existing = HostList::<BigInt>::new(HostListToken::Stored(0));
1033            let retained = HostCall::<TestHostProfile, Counter, List>::new(&mut runtime)
1034                .provider_list(existing, IntListDecoder);
1035
1036            assert_eq!(retained.len(), 1);
1037            assert_eq!(retained.get(0), Some(BigInt::from(1)));
1038        }
1039
1040        let arguments = CallArguments::new(Vec::new(), Vec::new());
1041        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
1042        let existing = HostList::<BigInt>::new(HostListToken::Stored(0));
1043
1044        let returned = HostCall::<TestHostProfile, Counter, List>::new(&mut runtime)
1045            .return_value(existing)
1046            .token;
1047
1048        assert_eq!(returned.family, HostValueFamily::List);
1049        assert_eq!(runtime.list_builds(), 0);
1050
1051        HostCall::<TestHostProfile, Counter, List>::new(&mut runtime)
1052            .return_list([BigInt::from(1), BigInt::from(2)]);
1053        assert_eq!(runtime.list_builds(), 1);
1054    }
1055
1056    #[test]
1057    fn host_call_invokes_and_completes_typed_function_handles() {
1058        type Arguments = HostTypeList<BigInt, HostTypeListEnd>;
1059        type Function = HostFunctionType<Arguments, BigInt>;
1060
1061        let mut state = TestRunState::default();
1062        let arguments = CallArguments::new(Vec::new(), Vec::new());
1063        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
1064        let callable = HostCallable::<Arguments, BigInt>::new(HostFunctionToken(0));
1065        let returned = HostCall::<TestHostProfile, Counter, BigInt>::new(&mut runtime)
1066            .invoke(callable, (BigInt::from(7), ()))
1067            .expect("test runtime should return the first callback argument");
1068
1069        assert_eq!(returned, BigInt::from(0));
1070        assert_eq!(
1071            runtime.completed(),
1072            Some(&HostScopedValue::Int(BigInt::from(7))),
1073        );
1074
1075        let completion = HostCall::<TestHostProfile, Counter, Function>::new(&mut runtime)
1076            .return_value(callable)
1077            .token;
1078        assert_eq!(completion.family, HostValueFamily::Function);
1079        assert_eq!(
1080            runtime.completed(),
1081            Some(&HostScopedValue::Function(HostFunctionToken(0))),
1082        );
1083
1084        let empty = HostCallable::<HostTypeListEnd, ()>::new(HostFunctionToken(1));
1085        HostCall::<TestHostProfile, Counter, ()>::new(&mut runtime)
1086            .invoke(empty, ())
1087            .expect("zero-argument test callback should return Nil");
1088    }
1089}