1use crate::host::{
2 ExternalPayloadLease, ExternalPayloadView, HostExternalStore, HostList, HostType,
3};
4use crate::runtime::{
5 StoredRuntimeList, StoredRuntimeListCustomFields, StoredRuntimeListItem,
6 StoredRuntimeListTupleItems,
7};
8use std::marker::PhantomData;
9use std::ops::Deref;
10
11pub struct List<Item, Context = MissingListContext> {
18 context: Context,
19 item: PhantomData<fn() -> Item>,
20}
21
22#[doc(hidden)]
23pub struct MissingListContext;
24
25#[doc(hidden)]
27pub struct ProviderListContext<'call, HostItem, Decoder> {
28 host: HostList<'call, HostItem>,
29 retained: StoredRuntimeList,
30 decoder: Decoder,
31}
32
33#[doc(hidden)]
35pub struct ProviderInputListContext<Decoder> {
36 retained: StoredRuntimeList,
37 decoder: Decoder,
38}
39
40#[doc(hidden)]
42pub trait ProviderListItemDecoder<Item> {
43 type View;
44
45 fn decode(&self, value: ProviderListItemValue<'_>) -> Self::View;
46}
47
48#[doc(hidden)]
50pub struct ProviderListItemValue<'value> {
51 value: StoredRuntimeListItem<'value>,
52}
53
54#[doc(hidden)]
56pub struct ProviderExternalPayloadAccess<Payload> {
57 store: HostExternalStore<Payload>,
58}
59
60#[doc(hidden)]
62pub struct ProviderExternalItem<Payload> {
63 value: ExternalPayloadView<Payload>,
64 lease: ExternalPayloadLease,
65}
66
67#[doc(hidden)]
69#[derive(Clone, Copy)]
70pub struct ProviderScalarListDecoder<Scalar>(PhantomData<fn() -> Scalar>);
71
72#[doc(hidden)]
74pub struct ProviderExternalListDecoder<Payload> {
75 access: ProviderExternalPayloadAccess<Payload>,
76}
77
78impl<Payload: 'static> Clone for ProviderExternalListDecoder<Payload> {
79 fn clone(&self) -> Self {
80 Self {
81 access: self.access.clone(),
82 }
83 }
84}
85
86#[doc(hidden)]
88pub struct ProviderListTupleItems<'value> {
89 values: StoredRuntimeListTupleItems<'value>,
90}
91
92#[doc(hidden)]
94pub struct ProviderListCustomFields<'value> {
95 fields: StoredRuntimeListCustomFields<'value>,
96}
97
98impl<'call, Item, HostItem, Decoder> List<Item, ProviderListContext<'call, HostItem, Decoder>>
99where
100 HostItem: HostType,
101 Decoder: ProviderListItemDecoder<Item>,
102{
103 #[expect(
105 clippy::len_without_is_empty,
106 reason = "the first provider List slice intentionally exposes only len and get"
107 )]
108 pub fn len(&self) -> usize {
109 self.context.retained.len()
110 }
111
112 pub fn get(&self, index: usize) -> Option<Decoder::View> {
114 self.context.retained.decode_item(index, |value| {
115 self.context.decoder.decode(ProviderListItemValue { value })
116 })
117 }
118
119 #[doc(hidden)]
120 pub fn __geam_into_context(self) -> ProviderListContext<'call, HostItem, Decoder> {
121 self.context
122 }
123}
124
125impl<Item, Decoder> List<Item, ProviderInputListContext<Decoder>>
126where
127 Decoder: ProviderListItemDecoder<Item>,
128{
129 #[expect(
131 clippy::len_without_is_empty,
132 reason = "the provider List slice intentionally exposes only len and get"
133 )]
134 pub fn len(&self) -> usize {
135 self.context.retained.len()
136 }
137
138 pub fn get(&self, index: usize) -> Option<Decoder::View> {
140 self.context.retained.decode_item(index, |value| {
141 self.context.decoder.decode(ProviderListItemValue { value })
142 })
143 }
144}
145
146impl<'call, HostItem, Decoder> ProviderListContext<'call, HostItem, Decoder>
147where
148 HostItem: HostType,
149{
150 pub(crate) fn new(
151 host: HostList<'call, HostItem>,
152 retained: StoredRuntimeList,
153 decoder: Decoder,
154 ) -> Self {
155 Self {
156 host,
157 retained,
158 decoder,
159 }
160 }
161
162 pub(crate) fn into_list<Item>(self) -> List<Item, Self> {
163 List {
164 context: self,
165 item: PhantomData,
166 }
167 }
168
169 #[doc(hidden)]
170 pub fn into_host(self) -> HostList<'call, HostItem> {
171 self.host
172 }
173}
174
175impl<Decoder> ProviderInputListContext<Decoder> {
176 pub(crate) fn new<Item>(retained: StoredRuntimeList, decoder: Decoder) -> List<Item, Self> {
177 List {
178 context: Self { retained, decoder },
179 item: PhantomData,
180 }
181 }
182}
183
184impl<'value> ProviderListItemValue<'value> {
185 #[doc(hidden)]
186 #[allow(private_bounds)]
187 pub fn into_scalar<Scalar>(self) -> Scalar
188 where
189 Scalar: ProviderListScalar,
190 {
191 Scalar::decode(self.value)
192 }
193
194 #[doc(hidden)]
195 pub fn into_external<Payload>(
196 self,
197 access: &ProviderExternalPayloadAccess<Payload>,
198 ) -> ProviderExternalItem<Payload>
199 where
200 Payload: 'static,
201 {
202 let lease = self.value.into_external_lease();
203 let value = access.store.view(&lease);
204 ProviderExternalItem { value, lease }
205 }
206
207 #[doc(hidden)]
208 pub fn into_tuple(self) -> ProviderListTupleItems<'value> {
209 ProviderListTupleItems {
210 values: self.value.into_tuple_items(),
211 }
212 }
213
214 #[doc(hidden)]
215 pub fn into_custom(self) -> ProviderListCustomFields<'value> {
216 ProviderListCustomFields {
217 fields: self.value.into_custom_fields(),
218 }
219 }
220
221 #[doc(hidden)]
222 pub fn into_list<Item, Decoder>(
223 self,
224 decoder: Decoder,
225 ) -> List<Item, ProviderInputListContext<Decoder>>
226 where
227 Decoder: ProviderListItemDecoder<Item>,
228 {
229 ProviderInputListContext::new(self.value.into_list(), decoder)
230 }
231}
232
233impl ProviderListTupleItems<'_> {
234 #[doc(hidden)]
235 pub fn take_item(&mut self, index: usize) -> ProviderListItemValue<'_> {
236 ProviderListItemValue {
237 value: self.values.take_item(index),
238 }
239 }
240}
241
242impl ProviderListCustomFields<'_> {
243 #[doc(hidden)]
244 pub fn constructor(&self) -> usize {
245 self.fields.constructor()
246 }
247
248 #[doc(hidden)]
249 pub fn take_field(&mut self, index: usize) -> ProviderListItemValue<'_> {
250 ProviderListItemValue {
251 value: self.fields.take_field(index),
252 }
253 }
254}
255
256impl<Payload: 'static> ProviderExternalPayloadAccess<Payload> {
257 pub(crate) fn new(store: &HostExternalStore<Payload>) -> Self {
258 Self {
259 store: store.clone_handle(),
260 }
261 }
262}
263
264impl<Payload: 'static> Clone for ProviderExternalPayloadAccess<Payload> {
265 fn clone(&self) -> Self {
266 Self {
267 store: self.store.clone_handle(),
268 }
269 }
270}
271
272impl<Payload> Deref for ProviderExternalItem<Payload> {
273 type Target = Payload;
274
275 fn deref(&self) -> &Self::Target {
276 &self.value
277 }
278}
279
280impl<Payload> ProviderExternalItem<Payload> {
281 pub(crate) fn new(value: ExternalPayloadView<Payload>, lease: ExternalPayloadLease) -> Self {
282 Self { value, lease }
283 }
284
285 pub(crate) fn into_lease(self) -> ExternalPayloadLease {
286 self.lease
287 }
288}
289
290impl<Scalar> ProviderScalarListDecoder<Scalar> {
291 pub(crate) fn new() -> Self {
292 Self(PhantomData)
293 }
294}
295
296impl<Scalar> ProviderListItemDecoder<Scalar> for ProviderScalarListDecoder<Scalar>
297where
298 Scalar: ProviderListScalar,
299{
300 type View = Scalar;
301
302 fn decode(&self, value: ProviderListItemValue<'_>) -> Self::View {
303 value.into_scalar()
304 }
305}
306
307impl<Payload: 'static> ProviderExternalListDecoder<Payload> {
308 pub fn new(access: ProviderExternalPayloadAccess<Payload>) -> Self {
309 Self { access }
310 }
311}
312
313impl<Payload: 'static> ProviderListItemDecoder<Payload> for ProviderExternalListDecoder<Payload> {
314 type View = ProviderExternalItem<Payload>;
315
316 fn decode(&self, value: ProviderListItemValue<'_>) -> Self::View {
317 value.into_external(&self.access)
318 }
319}
320
321trait ProviderListScalar: Sized {
322 fn decode(value: StoredRuntimeListItem) -> Self;
323}
324
325macro_rules! provider_list_scalar {
326 ($type:ty, $method:ident) => {
327 impl ProviderListScalar for $type {
328 fn decode(value: StoredRuntimeListItem) -> Self {
329 value.$method()
330 }
331 }
332 };
333}
334
335provider_list_scalar!(num_bigint::BigInt, into_int);
336provider_list_scalar!(f64, into_float);
337provider_list_scalar!(ecow::EcoString, into_string);
338provider_list_scalar!(crate::BitArrayValue, into_bit_array);
339provider_list_scalar!(char, into_utf_codepoint);
340provider_list_scalar!(bool, into_bool);
341provider_list_scalar!((), into_nil);
342
343#[cfg(test)]
344mod tests {
345 use super::{ProviderListContext, ProviderListItemDecoder, ProviderListItemValue};
346 use crate::host::{HostList, HostListToken};
347 use crate::runtime::StoredRuntimeList;
348 use num_bigint::BigInt;
349
350 struct IntDecoder;
351
352 impl ProviderListItemDecoder<BigInt> for IntDecoder {
353 type View = BigInt;
354
355 fn decode(&self, value: ProviderListItemValue<'_>) -> Self::View {
356 value.into_scalar()
357 }
358 }
359
360 #[test]
361 fn retained_list_length_and_indexing_decode_only_requested_items() {
362 let retained = StoredRuntimeList::test_ints(vec![1.into(), 2.into()]);
363 let host = HostList::<BigInt>::new(HostListToken::Stored(0));
364 let list = ProviderListContext::new(host, retained, IntDecoder).into_list::<BigInt>();
365
366 assert_eq!(list.context.retained.item_reads(), 0);
367 assert_eq!(list.len(), 2);
368 assert_eq!(list.context.retained.item_reads(), 0);
369 assert_eq!(list.get(1), Some(2.into()));
370 assert_eq!(list.context.retained.item_reads(), 1);
371 assert_eq!(list.get(2), None);
372 assert_eq!(list.context.retained.item_reads(), 2);
373 }
374}