1use crate::host::{
2 HostCall, HostExternalEquality, HostExternalHashing, HostExternalInspection, HostListType,
3 HostProfile, HostProvider, HostStoredDynamic, HostStoredType, HostStoredValue, HostType,
4 HostTypeIndex0, HostTypeIndexNext,
5};
6use crate::provider::{
7 List, ProviderConstructions, ProviderExternalDeclaration, ProviderInputValue,
8 ProviderListContext, ProviderListInputCodec, ProviderListInputValue, ProviderNoConstructions,
9 ProviderOutputValue, ProviderStoredOwner, ProviderValue, ProviderValueContext, Value,
10};
11use ecow::EcoString;
12use std::marker::PhantomData;
13
14pub type Equality<'value> = HostExternalEquality<'value>;
16
17pub type Hashing<'value> = HostExternalHashing<'value>;
19
20pub type Inspection<'value> = HostExternalInspection<'value>;
22
23pub struct Retained<Owner, Index> {
29 value: HostStoredValue<HostStoredType<Index>>,
30 owner: PhantomData<fn() -> Owner>,
31}
32
33pub struct StoredDynamic<Owner> {
39 value: HostStoredDynamic,
40 owner: PhantomData<fn() -> Owner>,
41}
42
43pub type External<Payload> = crate::provider::ProviderExternalItem<Payload>;
49
50#[doc(hidden)]
52pub trait ProviderDynamicValue<'call, Profile, Provider, Return>
53where
54 Profile: HostProfile,
55 Provider: HostProvider<Profile>,
56 Return: HostType,
57{
58 type Host: HostType;
59
60 fn into_host(
61 self,
62 call: &mut HostCall<'call, Profile, Provider, Return>,
63 ) -> <Self::Host as HostType>::Value<'call>;
64}
65
66impl<'call, Profile, Provider, Return, Type, Host>
67 ProviderDynamicValue<'call, Profile, Provider, Return>
68 for Value<Type, ProviderValueContext<'call, Host>>
69where
70 Profile: HostProfile,
71 Provider: HostProvider<Profile>,
72 Return: HostType,
73 Host: HostType,
74{
75 type Host = Host;
76
77 fn into_host(
78 self,
79 _call: &mut HostCall<'call, Profile, Provider, Return>,
80 ) -> <Self::Host as HostType>::Value<'call> {
81 self.into_host()
82 }
83}
84
85impl<'call, Profile, Provider, Return, Type> ProviderDynamicValue<'call, Profile, Provider, Return>
86 for Type
87where
88 Profile: HostProfile,
89 Provider: HostProvider<Profile>,
90 Return: HostType,
91 Type: ProviderValue<OutputRequirements = ProviderNoConstructions>
92 + ProviderOutputValue<Profile, Provider, Return>,
93{
94 type Host = Type::Host;
95
96 fn into_host(
97 self,
98 call: &mut HostCall<'call, Profile, Provider, Return>,
99 ) -> <Self::Host as HostType>::Value<'call> {
100 self.into_host(call, &ProviderConstructions::none())
101 }
102}
103
104impl<'call, Profile, Provider, Return, Item, HostItem, Decoder>
105 ProviderDynamicValue<'call, Profile, Provider, Return>
106 for List<Item, ProviderListContext<'call, HostItem, Decoder>>
107where
108 Profile: HostProfile,
109 Provider: HostProvider<Profile>,
110 Return: HostType,
111 HostItem: HostType,
112 Decoder: crate::provider::ProviderListItemDecoder<Item>,
113{
114 type Host = HostListType<HostItem>;
115
116 fn into_host(
117 self,
118 _call: &mut HostCall<'call, Profile, Provider, Return>,
119 ) -> <Self::Host as HostType>::Value<'call> {
120 self.__geam_into_context().into_host()
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub enum DynamicKind {
127 Int,
128 Float,
129 String,
130 BitArray,
131 UtfCodepoint,
132 Bool,
133 Nil,
134 List,
135 Tuple,
136 Custom,
137 External,
138 Function,
139}
140
141impl DynamicKind {
142 fn from_family(family: crate::provider_support::HostStoredValueFamily) -> Self {
143 match family {
144 crate::provider_support::HostStoredValueFamily::Int => Self::Int,
145 crate::provider_support::HostStoredValueFamily::Float => Self::Float,
146 crate::provider_support::HostStoredValueFamily::String => Self::String,
147 crate::provider_support::HostStoredValueFamily::BitArray => Self::BitArray,
148 crate::provider_support::HostStoredValueFamily::UtfCodepoint => Self::UtfCodepoint,
149 crate::provider_support::HostStoredValueFamily::Bool => Self::Bool,
150 crate::provider_support::HostStoredValueFamily::Nil => Self::Nil,
151 crate::provider_support::HostStoredValueFamily::List => Self::List,
152 crate::provider_support::HostStoredValueFamily::Tuple => Self::Tuple,
153 crate::provider_support::HostStoredValueFamily::Custom => Self::Custom,
154 crate::provider_support::HostStoredValueFamily::External => Self::External,
155 crate::provider_support::HostStoredValueFamily::Function => Self::Function,
156 }
157 }
158}
159
160#[doc(hidden)]
162pub trait ProviderDynamicInput<Profile, Provider, Return>
163where
164 Profile: HostProfile,
165 Provider: HostProvider<Profile>,
166 Return: HostType,
167{
168 type Host: HostType;
169 type View<'call>;
170
171 fn from_host<'call>(
172 call: &mut HostCall<'call, Profile, Provider, Return>,
173 value: <Self::Host as HostType>::Value<'call>,
174 ) -> Self::View<'call>;
175}
176
177impl<Profile, Provider, Return, Type> ProviderDynamicInput<Profile, Provider, Return> for Type
178where
179 Profile: HostProfile,
180 Provider: HostProvider<Profile>,
181 Return: HostType,
182 Type: ProviderValue,
183 Type::Input: ProviderInputValue<Profile, Provider, Return> + ProviderValue<Host = Type::Host>,
184{
185 type Host = Type::Host;
186 type View<'call> = Type::Input;
187
188 fn from_host<'call>(
189 call: &mut HostCall<'call, Profile, Provider, Return>,
190 value: <Self::Host as HostType>::Value<'call>,
191 ) -> Self::View<'call> {
192 Type::Input::from_host(call, value)
193 }
194}
195
196impl<Profile, Provider, Return, Item> ProviderDynamicInput<Profile, Provider, Return> for List<Item>
197where
198 Profile: HostProfile,
199 Provider: HostProvider<Profile>,
200 Return: HostType,
201 Item: ProviderValue<ListInput = Item>,
202 Item::ListInput: ProviderListInputCodec<Profile>,
203{
204 type Host = HostListType<Item::Host>;
205 type View<'call> = List<
206 Item,
207 ProviderListContext<
208 'call,
209 Item::Host,
210 <Item::ListInput as ProviderListInputValue>::Decoder,
211 >,
212 >;
213
214 fn from_host<'call>(
215 call: &mut HostCall<'call, Profile, Provider, Return>,
216 value: <Self::Host as HostType>::Value<'call>,
217 ) -> Self::View<'call> {
218 let decoder = <Item::ListInput as ProviderListInputCodec<Profile>>::decoder(call);
219 call.provider_list(value, decoder)
220 }
221}
222
223pub type Index0 = HostTypeIndex0;
225
226pub type Next<Index> = HostTypeIndexNext<Index>;
228
229pub trait RetainedExternalPayload: 'static {
232 fn source_equal(&self, context: &Equality<'_>, other: &Self) -> bool;
233
234 fn source_hash(&self, context: &Hashing<'_>) -> u64;
235
236 fn inspect(&self, context: &Inspection<'_>) -> EcoString;
237}
238
239impl<Owner, Index> Retained<Owner, Index>
240where
241 Owner: ProviderStoredOwner,
242{
243 pub(crate) fn new(value: HostStoredValue<HostStoredType<Index>>) -> Self {
244 Self {
245 value,
246 owner: PhantomData,
247 }
248 }
249
250 pub(crate) fn host(&self) -> &HostStoredValue<HostStoredType<Index>> {
251 &self.value
252 }
253
254 pub fn source_equal(&self, context: &Equality<'_>, other: &Self) -> bool {
256 context.stored_values_equal(&self.value, &other.value)
257 }
258
259 pub fn source_hash(&self, context: &Hashing<'_>) -> u64 {
261 context.stored_value_hash(&self.value)
262 }
263
264 pub fn inspect(&self, context: &Inspection<'_>) -> EcoString {
266 context.inspect_stored_value(&self.value)
267 }
268}
269
270impl<Owner> StoredDynamic<Owner>
271where
272 Owner: ProviderStoredOwner,
273{
274 pub(crate) fn new(value: HostStoredDynamic) -> Self {
275 Self {
276 value,
277 owner: PhantomData,
278 }
279 }
280
281 pub(crate) fn host(&self) -> &HostStoredDynamic {
282 &self.value
283 }
284
285 pub fn kind(&self) -> DynamicKind {
287 DynamicKind::from_family(self.value.value_family())
288 }
289
290 pub fn is_external<Declaration>(&self) -> bool
292 where
293 Declaration: ProviderExternalDeclaration,
294 {
295 self.value.has_external_schema::<Declaration::Schema>()
296 }
297
298 #[expect(
302 clippy::result_large_err,
303 reason = "non-tuples retain the original value without another heap allocation"
304 )]
305 pub fn into_tuple_items(self) -> Result<Box<[Self]>, Self> {
306 self.value.map_tuple_items(Self::new).map_err(Self::new)
307 }
308
309 pub fn source_equal(&self, context: &Equality<'_>, other: &Self) -> bool {
311 context.dynamic_values_equal(&self.value, &other.value)
312 }
313
314 pub fn source_hash(&self, context: &Hashing<'_>) -> u64 {
316 context.dynamic_value_hash(&self.value)
317 }
318
319 pub fn inspect(&self, context: &Inspection<'_>) -> EcoString {
321 context.inspect_dynamic_value(&self.value)
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::{DynamicKind, Index0, Retained};
328 use crate::host::{
329 HostExternalEquality, HostExternalHashing, HostExternalInspection, HostStoredType,
330 HostStoredValue,
331 };
332 use crate::runtime::StoredRuntimeValue;
333
334 struct Payload;
335
336 impl crate::provider::ProviderStoredOwner for Payload {}
337
338 fn retained(value: i64) -> Retained<Payload, Index0> {
339 Retained::new(HostStoredValue::<HostStoredType<Index0>>::new(
340 StoredRuntimeValue::test_int(value.into()),
341 ))
342 }
343
344 #[test]
345 fn dynamic_kind_covers_every_retained_runtime_family() {
346 use crate::provider_support::HostStoredValueFamily;
347
348 assert_eq!(
349 DynamicKind::from_family(HostStoredValueFamily::Int),
350 DynamicKind::Int
351 );
352 assert_eq!(
353 DynamicKind::from_family(HostStoredValueFamily::Float),
354 DynamicKind::Float
355 );
356 assert_eq!(
357 DynamicKind::from_family(HostStoredValueFamily::String),
358 DynamicKind::String
359 );
360 assert_eq!(
361 DynamicKind::from_family(HostStoredValueFamily::BitArray),
362 DynamicKind::BitArray,
363 );
364 assert_eq!(
365 DynamicKind::from_family(HostStoredValueFamily::UtfCodepoint),
366 DynamicKind::UtfCodepoint,
367 );
368 assert_eq!(
369 DynamicKind::from_family(HostStoredValueFamily::Bool),
370 DynamicKind::Bool
371 );
372 assert_eq!(
373 DynamicKind::from_family(HostStoredValueFamily::Nil),
374 DynamicKind::Nil
375 );
376 assert_eq!(
377 DynamicKind::from_family(HostStoredValueFamily::List),
378 DynamicKind::List
379 );
380 assert_eq!(
381 DynamicKind::from_family(HostStoredValueFamily::Tuple),
382 DynamicKind::Tuple
383 );
384 assert_eq!(
385 DynamicKind::from_family(HostStoredValueFamily::Custom),
386 DynamicKind::Custom
387 );
388 assert_eq!(
389 DynamicKind::from_family(HostStoredValueFamily::External),
390 DynamicKind::External,
391 );
392 assert_eq!(
393 DynamicKind::from_family(HostStoredValueFamily::Function),
394 DynamicKind::Function,
395 );
396 }
397
398 #[test]
399 fn retained_values_delegate_each_source_operation_to_its_narrow_context() {
400 let first = retained(7);
401 let different = retained(8);
402 let stored_equal =
403 |left: &StoredRuntimeValue, right: &StoredRuntimeValue| std::ptr::eq(left, right);
404 let stored_hash = |_: &StoredRuntimeValue| 17;
405 let stored_inspect = |_: &StoredRuntimeValue| "Int(7)".into();
406 let equality = HostExternalEquality::new(&stored_equal);
407 let hashing = HostExternalHashing::new(&stored_hash);
408 let inspection = HostExternalInspection::new(&stored_inspect);
409
410 assert!(first.source_equal(&equality, &first));
411 assert!(!first.source_equal(&equality, &different));
412 assert_eq!(first.source_hash(&hashing), 17);
413 assert_eq!(first.inspect(&inspection), "Int(7)");
414 }
415}